Example #1
0
static void InitTris(void)
{
  INDEX iR, iC;
  INDEX ctVx = _ctR*_ctC;
  _avtx.Push(ctVx);
  _atex.Push(ctVx);
  _acol.Push(ctVx);
  for( iR=0; iR<_ctR; iR++) {
    for( iC=0; iC<_ctC; iC++) {
      INDEX ivx = iR*_ctC+iC;
      _avtx[ivx].x =  FLOAT(iC) / _ctC*4 -2.0f;
      _avtx[ivx].y = -FLOAT(iR) / _ctR*4 +2.0f;
      _avtx[ivx].z = -1.0f;
      _atex[ivx].st.s = (iC+iR) % 2;
      _atex[ivx].st.t = (iR)    % 2;
      _acol[ivx].ul.abgr = 0xFFFFFFFF;
    }
  }

  INDEX ctTri = (_ctR-1)*(_ctC-1)*2;
  _aiElements.Push(ctTri*3);
  for( iR=0; iR<_ctR-1; iR++) {
    for( iC=0; iC<_ctC-1; iC++) {
      INDEX iq = iR*(_ctC-1)+iC;
      _aiElements[iq*6+0] = (iR+1) * _ctC + (iC+0);
      _aiElements[iq*6+1] = (iR+1) * _ctC + (iC+1);
      _aiElements[iq*6+2] = (iR+0) * _ctC + (iC+0);
      _aiElements[iq*6+3] = (iR+0) * _ctC + (iC+0);
      _aiElements[iq*6+4] = (iR+1) * _ctC + (iC+1);
      _aiElements[iq*6+5] = (iR+0) * _ctC + (iC+1);
    }
  }
}
static void BatchTile(INDEX itt)
{
  CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
  ASSERT(tt.GetVertices().Count()==9);
  ASSERT(tt.GetIndices().Count()==24);

  INDEX ctDelayedVertices = _avDelayedVertices.Count();
  
  GFXVertex4  *pavVertices        = &tt.GetVertices()[0];
  GFXTexCoord *pauvTexCoords      = &tt.GetTexCoords()[0];
  GFXTexCoord *pauvShadowMapTC    = &tt.GetShadowMapTC()[0];
  INDEX       *paiIndices         = &tt.GetIndices()[0];

  GFXVertex4  *pavDelVertices     = _avDelayedVertices.Push(9);
  GFXTexCoord *pauvDelTexCoords   = _auvDelayedTexCoords.Push(9);
  GFXTexCoord *pauvDelShadowMapTC = _auvDelayedShadowMapTC.Push(9);
  INDEX       *paiDelIndices      = _aiDelayedIndices.Push(24);

  // for each vertex in tile
  for(INDEX ivx=0;ivx<9;ivx++) {
    // copy vertex, texcoord & shadow map texcoord to delayed array
    pavDelVertices[ivx]     = pavVertices[ivx];
    pauvDelTexCoords[ivx]   = pauvTexCoords[ivx];
    pauvDelShadowMapTC[ivx] = pauvShadowMapTC[ivx];
  }
  // for each index in tile
  for(INDEX iind=0;iind<24;iind++) {
    // reindex indice for new arrays
    paiDelIndices[iind] = paiIndices[iind] + ctDelayedVertices;
  }

  _ctDelayedNodes++;
}
static void RenderHazeLayer(INDEX itt)
{
  FLOAT3D vObjPosition = _ptrTerrain->tr_penEntity->en_plPlacement.pl_PositionVector;

  _fHazeAdd  = -_haze_hp.hp_fNear;
  _fHazeAdd += _vViewer(1) * (vObjPosition(1) - _aprProjection->pr_vViewerPosition(1));
  _fHazeAdd += _vViewer(2) * (vObjPosition(2) - _aprProjection->pr_vViewerPosition(2));
  _fHazeAdd += _vViewer(3) * (vObjPosition(3) - _aprProjection->pr_vViewerPosition(3));

  GFXVertex *pvVtx;
  INDEX     *piIndices;
  INDEX ctVertices;
  INDEX ctIndices;
  // if this is tile 
  if(itt>=0) {
    CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
    pvVtx      = &tt.GetVertices()[0];
    piIndices  = &tt.GetIndices()[0];
    ctVertices = tt.GetVertices().Count();
    ctIndices  = tt.GetIndices().Count();
  // else this are batched tiles
  } else {
    pvVtx      = &_avDelayedVertices[0];
    piIndices  = &_aiDelayedIndices[0];
    ctVertices = _avDelayedVertices.Count();
    ctIndices  = _aiDelayedIndices.Count();
  }

  GFXTexCoord *pfHazeTC  = _atcHaze.Push(ctVertices);
  GFXColor    *pcolHaze  = _acolHaze.Push(ctVertices);

  const COLOR colH = AdjustColor( _haze_hp.hp_colColor, _slTexHueShift, _slTexSaturation);
  GFXColor colHaze(colH);
  // for each vertex in tile
  for(INDEX ivx=0;ivx<ctVertices;ivx++) {
    GetHazeMapInVertex(pvVtx[ivx],pfHazeTC[ivx]);
    pcolHaze[ivx] = colHaze;
  }

  // render haze layer
  gfxDepthFunc(GFX_EQUAL);
  gfxSetTextureWrapping( GFX_CLAMP, GFX_CLAMP);
  gfxSetTexture( _haze_ulTexture, _haze_tpLocal);
  gfxSetTexCoordArray(pfHazeTC, FALSE);
  gfxSetColorArray(pcolHaze);
  gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
  gfxEnableBlend();
  gfxDrawElements(ctIndices,piIndices);
  gfxDepthFunc(GFX_LESS_EQUAL);

  _atcHaze.PopAll();
  _acolHaze.PopAll();
}
Example #4
0
// Get empty color array for modifying
GFXColor *shaGetNewColorArray(void)
{
  ASSERT(_ctVertices!=0);
  _acolVtxModifyColors.PopAll();
  _acolVtxModifyColors.Push(_ctVertices);
  return &_acolVtxModifyColors[0];
}
Example #5
0
static void LoadOneFile(const CTFileName &fnm)
{
  try {
    // open the file
    CTFileStream strm;
    strm.Open_t(fnm);

    // count number of lines
    INDEX ctLines = 0;
    while(!strm.AtEOF()) {
      CTString strLine;
      strm.GetLine_t(strLine);
      ctLines++;
    }
    strm.SetPos_t(0);

    // allocate that much
    CTString *astr = _astrCredits.Push(ctLines);
    // load all lines
    for(INDEX iLine = 0; iLine<ctLines && !strm.AtEOF(); iLine++) {
      strm.GetLine_t(astr[iLine]);
    }

    strm.Close();

    _bCreditsOn = TRUE;
  } catch (char *strError) {
    CPrintF("%s\n", strError);
  }
}
Example #6
0
// Get empty texcoords array for modifying
GFXTexCoord *shaGetNewTexCoordArray(void)
{
  ASSERT(_ctVertices!=0);
  _uvUVMapForModify.PopAll();
  _uvUVMapForModify.Push(_ctVertices);
  return &_uvUVMapForModify[0];
}
Example #7
0
// Get empty vertex array for modifying
GFXVertex *shaGetNewVertexArray(void)
{
  ASSERT(_ctVertices!=0);
  _vModifyVertices.PopAll();
  _vModifyVertices.Push(_ctVertices);
  return &_vModifyVertices[0];
}
Example #8
0
// update current active message category
static void UpdateType(BOOL bForce=FALSE)
{
  if (_cmtCurrentType==_cmtWantedType && !bForce) {
    return;
  }

  // cleare message cache
  _acmMessages.Clear();
  // for each player's message
  CDynamicStackArray<CCompMessageID> &acmiMsgs = _ppenPlayer->m_acmiMessages;
  for(INDEX i=0; i<acmiMsgs.Count(); i++) {
    CCompMessageID &cmi = acmiMsgs[i];
    // if it is of given type
    if (cmi.cmi_cmtType == _cmtWantedType) {
      // add it to cache
      CCompMessage &cm = _acmMessages.Push();
      cm.SetMessage(&cmi);
    }
  }
  if (!bForce) {
    _cmtCurrentType=_cmtWantedType;
    _iFirstMessageOnScreen = -1;
    _iWantedFirstMessageOnScreen = 0;
    _iActiveMessage = 0;
    _iLastActiveMessage = -2;
    _iTextLineOnScreen = 0;
    LastUnreadMessage();
    UpdateFirstOnScreen();
  }
}
Example #9
0
// make array of entity offsets in a block
void MakeInfos(CStaticStackArray<EntityBlockInfo> &aebi, 
               UBYTE *pubBlock, SLONG slSize, UBYTE *pubFirst, UBYTE *&pubEnd)
{
  // clear all offsets
  aebi.PopAll();

  // until end of block
  UBYTE *pub = pubFirst;
  while (pub<pubBlock+slSize) {
    // if no more entities
    if (*(ULONG*)pub != ENT4) {
      pubEnd = pub;
      // stop
      return;
    }
    // remember it
    EntityBlockInfo &ebi = aebi.Push();
    ebi.ebi_slOffset = pub-pubBlock;
    pub+=sizeof(ULONG);

    // get id and size
    ULONG ulID = *(ULONG*)pub;
    ebi.ebi_ulID     = ulID;
    pub+=sizeof(ULONG);

    SLONG slSizeChunk = *(SLONG*)pub;
    pub+=sizeof(ULONG);
    ebi.ebi_slSize   = slSizeChunk+sizeof(SLONG)*3;

    pub+=slSizeChunk;
  }
}
static void FillConstColorArray(INDEX ctVertices)
{
  INDEX ctColors=_acolVtxConstColors.Count();
  _acolVtxConstColors.PopAll();
  _acolVtxConstColors.Push(ctVertices);
  // if requested array is larger then existing one
  if(ctVertices>ctColors) {
    memset(&_acolVtxConstColors[ctColors],255,(ctVertices-ctColors)*sizeof(GFXColor));
  }
}
Example #11
0
// Calculate lightning for given model
void shaCalculateLightForSpecular(void)
{
  ASSERT(_paNormals!=NULL);
  _acolVtxColors.PopAll();
  _acolVtxColors.Push(_ctVertices);

  GFXColor colModel   = (GFXColor)_colModel;   // Model color
  GFXColor &colAmbient = (GFXColor)_colAmbient; // Ambient color
  GFXColor &colLight   = (GFXColor)_colLight;   // Light color
  GFXColor &colSurface = (GFXColor)_colConstant; // shader color

  // colModel = MulColors(colModel.r,colSurface.abgr);
  colModel.MultiplyRGBA(colModel,colSurface);

  UBYTE ubColShift = 8;
  SLONG slar = colAmbient.r;
  SLONG slag = colAmbient.g;
  SLONG slab = colAmbient.b;

  if(shaOverBrightningEnabled()) {
    slar = ClampUp(slar,127L);
    slag = ClampUp(slag,127L);
    slab = ClampUp(slab,127L);
    ubColShift = 8;
  } else {
    slar*=2;
    slag*=2;
    slab*=2;
    ubColShift = 7;
  }

  // for each vertex color
  for(INDEX ivx=0;ivx<_ctVertices;ivx++) {
    // calculate vertex light
    FLOAT3D &vNorm = FLOAT3D(_paNormals[ivx].nx,_paNormals[ivx].ny,_paNormals[ivx].nz);
    FLOAT fDot = vNorm % _vLightDir;
    fDot = Clamp(fDot,0.0f,1.0f);
    SLONG slDot = NormFloatToByte(fDot);

    _acolVtxColors[ivx].r = ClampUp(colModel.r * (slar + ((colLight.r * slDot)>>ubColShift))>>8,255L);
    _acolVtxColors[ivx].g = ClampUp(colModel.g * (slag + ((colLight.g * slDot)>>ubColShift))>>8,255L);
    _acolVtxColors[ivx].b = ClampUp(colModel.b * (slab + ((colLight.b * slDot)>>ubColShift))>>8,255L);
    _acolVtxColors[ivx].a = slDot;//colModel.a;//slDot;
  }
  // Set current wertex array 
  _pcolVtxColors = &_acolVtxColors[0];
}
Example #12
0
// check point against depth buffer
extern BOOL CheckDepthPoint( const CDrawPort *pdp, PIX pixI, PIX pixJ, FLOAT f**K, INDEX iID, INDEX iMirrorLevel/*=0*/)
{
  // no raster?
  const CRaster *pra = pdp->dp_Raster;
  if( pra==NULL) return FALSE;
  // almoust out of raster?
  pixI += pdp->dp_MinI;
  pixJ += pdp->dp_MinJ;
  if( pixI<1 || pixJ<1 || pixI>pra->ra_Width-2 || pixJ>pra->ra_Height-2) return FALSE;

  // if shouldn't delay
  if( gap_iOptimizeDepthReads==0) {
    // just check immediately
    DepthInfo di = { iID, pixI, pixJ, f**K, _iCheckIteration, iMirrorLevel, FALSE };
    UpdateDepthPointsVisibility( pdp, iMirrorLevel, &di, 1);
    return di.di_bVisible;
  }

  // for each stored point
  for( INDEX idi=0; idi<_adiDelayed.Count(); idi++) {
    DepthInfo &di = _adiDelayed[idi];
    // if same id
    if( di.di_iID == iID) {
      // remember parameters
      di.di_pixI = pixI;
      di.di_pixJ = pixJ;
      di.di_fOoK = f**K;
      di.di_iSwapLastRequest = _iCheckIteration;
      // return visibility
      return di.di_bVisible;
    }
  }
  // if not found...

  // create new one
  DepthInfo &di = _adiDelayed.Push();
  // remember parameters
  di.di_iID  = iID;
  di.di_pixI = pixI;
  di.di_pixJ = pixJ;
  di.di_fOoK = f**K;
  di.di_iSwapLastRequest = _iCheckIteration;
  di.di_iMirrorLevel = iMirrorLevel;
  di.di_bVisible = FALSE;
  // not visible by default
  return FALSE;
}
Example #13
0
  // fill remap array with indices of vertices in order how they appear per polygons
  FOREACHINDYNAMICCONTAINER(acmMaterials, ConversionMaterial, itcm)
  {
    // fill remap array with -1
    for( INDEX iRemap=0; iRemap<ctVertices; iRemap++)
    {
      aiRemap[iRemap] = -1;
    }
    // reset 'vertex in surface' counter
    INDEX ctvx = 0;

    // for each polygon in surface
    {FOREACHINDYNAMICCONTAINER(itcm->ms_Polygons, INDEX, itipol)
    {
      INDEX idxPol = *itipol;
      // for each vertex in polygon
      for(INDEX iVtx=0; iVtx<3; iVtx++)
      {
        // get vertex's index
        INDEX idxVtx = actTriangles[idxPol].ct_iVtx[iVtx];
        if( aiRemap[idxVtx] == -1)
        {
          aiRemap[idxVtx] = ctvx;
          ctvx++;
        }
      }
    }}

    INDEX ctOld = avDst.Count();
    // allocate new block of vertices used in this surface
    FLOAT3D *pavDst = avDst.Push( ctvx);

    // for each polygon in surface
    {FOREACHINDYNAMICCONTAINER(itcm->ms_Polygons, INDEX, itipol)
    {
      INDEX iPol=*itipol;
      // for each vertex in polygon
      for(INDEX iVtx=0; iVtx<3; iVtx++)
      {
        // get vertex's index
        INDEX idxVtx = actTriangles[iPol].ct_iVtx[iVtx];
        // get remapped index
        INDEX iRemap = aiRemap[idxVtx];
        // if cutting object
        if( bAsOpened)
        {
          // copy vertex coordinate
          pavDst[ iRemap] = avVertices[idxVtx];
        }
        // if creating unwrapped mapping
        else
        {
          // copy texture coordinate
          FLOAT3D vMap;
          vMap(1) = avTextureVertices[actTriangles[iPol].ct_iTVtx[iVtx]](1);
          vMap(2) = -avTextureVertices[actTriangles[iPol].ct_iTVtx[iVtx]](2);
          vMap(3) = 0;
          pavDst[ iRemap] = vMap;
        }
        // remap index of polygon vertex
        actTriangles[iPol].ct_iVtx[iVtx] = iRemap+ctOld;
      }
    }}
  }
extern void InternalShader_Mask(void)
{
  // need arrays and texture
  INDEX ctIdx = shaGetIndexCount();
  INDEX ctVtx = shaGetVertexCount();
  if( ctIdx==0 || ctVtx==0) return;
  INDEX *pidx = shaGetIndexArray();
  GFXVertex4  *pvtx = shaGetVertexArray();
  GFXTexCoord *ptex = shaGetUVMap(0);
  CTextureObject *pto = shaGetTexture(0);
  ASSERT( (ctIdx%3) == 0); // must have triangles?

  // prepare texture
  ULONG *pulTexFrame = NULL;
  PIX pixMipWidth=0, pixMipHeight=0;

  if( pto!=NULL && ptex!=NULL) {
    CTextureData *ptd = (CTextureData*)pto->GetData();
    if( ptd!=NULL && ptd->td_ptegEffect==NULL) {
      // fetch some texture params
      pulTexFrame  = ptd->td_pulFrames + (pto->GetFrame()*ptd->td_slFrameSize)/BYTES_PER_TEXEL;
      pixMipWidth  = ptd->GetPixWidth();
      pixMipHeight = ptd->GetPixHeight();
      // reload texture and keep in memory
      ptd->Force(TEX_STATIC);
    }
  }
  // initialize texture for usage thru render triangle routine
  SetTriangleTexture( pulTexFrame, pixMipWidth, pixMipHeight);

  // prepare projection
  const BOOL bPerspective = _aprProjection.IsPerspective();
  CPerspectiveProjection3D &prPerspective = (CPerspectiveProjection3D &)*_aprProjection;
  CParallelProjection3D &prParallel = (CParallelProjection3D &)*_aprProjection;
  FLOAT fCenterI, fCenterJ, fRatioI, fRatioJ, fStepI, fStepJ, fZoomI, fZoomJ; 
  FLOAT fFrontClipDistance, fBackClipDistance, f1oFrontClipDistance, f1oBackClipDistance, fDepthBufferFactor;

  if( bPerspective) {
    fCenterI = prPerspective.pr_ScreenCenter(1);
    fCenterJ = prPerspective.pr_ScreenCenter(2);
    fRatioI  = prPerspective.ppr_PerspectiveRatios(1);
    fRatioJ  = prPerspective.ppr_PerspectiveRatios(2);
    fFrontClipDistance   = -prPerspective.pr_NearClipDistance;
    fBackClipDistance    = -prPerspective.pr_FarClipDistance;
    f1oFrontClipDistance = -1.0f / prPerspective.pr_NearClipDistance;
    f1oBackClipDistance  = -1.0f / prPerspective.pr_FarClipDistance;
    fDepthBufferFactor   = prPerspective.pr_fDepthBufferFactor;
  } else {
    fCenterI = prParallel.pr_ScreenCenter(1);
    fCenterJ = prParallel.pr_ScreenCenter(2);
    fStepI   = prParallel.pr_vStepFactors(1);
    fStepJ   = prParallel.pr_vStepFactors(2);
    fZoomI   = prParallel.pr_vZoomFactors(1);
    fZoomJ   = prParallel.pr_vZoomFactors(2);
    fFrontClipDistance   = -prPerspective.pr_NearClipDistance;
    fBackClipDistance    = -prPerspective.pr_FarClipDistance;
    f1oFrontClipDistance = 1.0f;
    f1oBackClipDistance  = 1.0f;
    fDepthBufferFactor   = 1.0f;
  }

  // copy view space vertices, project 'em to screen space and mark clipping
  CStaticStackArray<TransformedVertexData> atvd;
  INDEX iVtx=0;
  for(; iVtx<ctVtx; iVtx++)
  {
    // copy viewspace and texture coords
    TransformedVertexData &tvd = atvd.Push();
    tvd.tvd_fX = pvtx[iVtx].x;
    tvd.tvd_fY = pvtx[iVtx].y;
    tvd.tvd_fZ = pvtx[iVtx].z;
    tvd.tvd_bClipped = FALSE;   // initially, vertex is not clipped

    // prepare screen coordinates
    if( bPerspective) {
      const FLOAT f1oZ = 1.0f / tvd.tvd_fZ;
      tvd.tvd_pv2.pv2_fI   = fCenterI + tvd.tvd_fX*fRatioI *f1oZ;
      tvd.tvd_pv2.pv2_fJ   = fCenterJ - tvd.tvd_fY*fRatioJ *f1oZ;
      tvd.tvd_pv2.pv2_f1oK = fDepthBufferFactor*f1oZ;
    } else {
      tvd.tvd_pv2.pv2_fI   = fCenterI + tvd.tvd_fX*fZoomI + tvd.tvd_fZ*fStepI;
      tvd.tvd_pv2.pv2_fJ   = fCenterJ - tvd.tvd_fY*fZoomJ - tvd.tvd_fZ*fStepJ;
      tvd.tvd_pv2.pv2_f1oK = 1;
    }

    // adjust texture coords (if any!)
    if( ptex!=NULL) {
      tvd.tvd_fU = ptex[iVtx].s;
      tvd.tvd_fV = ptex[iVtx].t;
      tvd.tvd_pv2.pv2_fUoK = tvd.tvd_fU * tvd.tvd_pv2.pv2_f1oK *pixMipWidth;
      tvd.tvd_pv2.pv2_fVoK = tvd.tvd_fV * tvd.tvd_pv2.pv2_f1oK *pixMipHeight;
    } else tvd.tvd_fU = tvd.tvd_fV = 0;

    // check clipping against horizontal screen boundaries and near clip plane
    if( tvd.tvd_pv2.pv2_fI<0 || tvd.tvd_pv2.pv2_fI>=_slMaskWidth
     || tvd.tvd_fZ>fFrontClipDistance || (fBackClipDistance<0 && tvd.tvd_fZ<fBackClipDistance)) {
      tvd.tvd_bClipped = TRUE;
    }
  }

  // lets clip and render - triangle by triangle
  for( INDEX iIdx=0; iIdx<ctIdx; iIdx+=3)
  {
    // get transformed triangle
    TransformedVertexData tvd[3];
    iVtx = pidx[iIdx+0];  tvd[0] = atvd[iVtx];
    iVtx = pidx[iIdx+1];  tvd[1] = atvd[iVtx];
    iVtx = pidx[iIdx+2];  tvd[2] = atvd[iVtx];

    // clipped?
    if( tvd[0].tvd_bClipped || tvd[1].tvd_bClipped || tvd[2].tvd_bClipped)
    {
      // create array of vertices for polygon clipped to near clip plane
      ctvxDst=0;
      INDEX ivx0=2;
      INDEX ivx1=0;
      {for( INDEX ivx=0; ivx<3; ivx++)
      {
        TransformedVertexData &tvd0 = tvd[ivx0];
        TransformedVertexData &tvd1 = tvd[ivx1];
        FLOAT fd0 = fFrontClipDistance-tvd0.tvd_fZ;
        FLOAT fd1 = fFrontClipDistance-tvd1.tvd_fZ;
        // if first vertex is in
        if( fd0>=0) {
          // add it to clip array
          ptvdDst[ctvxDst] = tvd0;
          ctvxDst++;
          // if second vertex is out
          if( fd1<0) {
            // add clipped vertex at exit
            TransformedVertexData &tvdClipped = ptvdDst[ctvxDst];
            ctvxDst++;
            FLOAT fF = fd1/(fd1-fd0);
            tvdClipped.tvd_fX = tvd1.tvd_fX - (tvd1.tvd_fX - tvd0.tvd_fX) *fF;
            tvdClipped.tvd_fY = tvd1.tvd_fY - (tvd1.tvd_fY - tvd0.tvd_fY) *fF;
            tvdClipped.tvd_fZ = fFrontClipDistance;
            tvdClipped.tvd_pv2.pv2_f1oK = fDepthBufferFactor * f1oFrontClipDistance;
            FLOAT fU = tvd1.tvd_fU - (tvd1.tvd_fU - tvd0.tvd_fU) *fF;
            FLOAT fV = tvd1.tvd_fV - (tvd1.tvd_fV - tvd0.tvd_fV) *fF;
            tvdClipped.tvd_pv2.pv2_fUoK = fU * tvdClipped.tvd_pv2.pv2_f1oK;
            tvdClipped.tvd_pv2.pv2_fVoK = fV * tvdClipped.tvd_pv2.pv2_f1oK;
          }
        // if first vertex is out (don't add it into clip array)
        } else {
          // if second vertex is in
          if( fd1>=0) {
            // add clipped vertex at entry
            TransformedVertexData &tvdClipped = ptvdDst[ctvxDst];
            ctvxDst++;
            FLOAT fF = fd0/(fd0-fd1);
            tvdClipped.tvd_fX = tvd0.tvd_fX - (tvd0.tvd_fX - tvd1.tvd_fX) *fF;
            tvdClipped.tvd_fY = tvd0.tvd_fY - (tvd0.tvd_fY - tvd1.tvd_fY) *fF;
            tvdClipped.tvd_fZ = fFrontClipDistance;
            tvdClipped.tvd_pv2.pv2_f1oK = fDepthBufferFactor * f1oFrontClipDistance;
            FLOAT fU = tvd0.tvd_fU - (tvd0.tvd_fU - tvd1.tvd_fU) *fF;
            FLOAT fV = tvd0.tvd_fV - (tvd0.tvd_fV - tvd1.tvd_fV) *fF;
            tvdClipped.tvd_pv2.pv2_fUoK = fU * tvdClipped.tvd_pv2.pv2_f1oK;
            tvdClipped.tvd_pv2.pv2_fVoK = fV * tvdClipped.tvd_pv2.pv2_f1oK;
          }
        }
        // proceed to next vertex in list (i.e. new pair of vertices)
        ivx0=ivx1;
        ivx1++;
      }}
      // swap buffers
      Swap( ptvdSrc, ptvdDst);
      Swap( ctvxSrc, ctvxDst);

      // if clipping to far clip plane is on
      if( fBackClipDistance<0) {
        ctvxDst=0;
        INDEX ivx0=ctvxSrc-1;
        INDEX ivx1=0;
        {for( INDEX ivx=0; ivx<ctvxSrc; ivx++)
        {
          TransformedVertexData &tvd0 = ptvdSrc[ivx0];
          TransformedVertexData &tvd1 = ptvdSrc[ivx1];
          FLOAT fd0 = tvd0.tvd_fZ-fBackClipDistance;
          FLOAT fd1 = tvd1.tvd_fZ-fBackClipDistance;
          // if first vertex is in
          if( fd0>=0) {
            // add it to clip array
            ptvdDst[ctvxDst] = tvd0;
            ctvxDst++;
            // if second vertex is out
            if( fd1<0) {
              // add clipped vertex at exit
              TransformedVertexData &tvdClipped = ptvdDst[ctvxDst];
              ctvxDst++;
              FLOAT fF = fd1/(fd1-fd0);
              tvdClipped.tvd_fX = tvd1.tvd_fX - (tvd1.tvd_fX - tvd0.tvd_fX) *fF;
              tvdClipped.tvd_fY = tvd1.tvd_fY - (tvd1.tvd_fY - tvd0.tvd_fY) *fF;
              tvdClipped.tvd_fZ = fBackClipDistance;
              tvdClipped.tvd_pv2.pv2_f1oK = fDepthBufferFactor * f1oBackClipDistance;
              FLOAT fU = tvd1.tvd_fU - (tvd1.tvd_fU - tvd0.tvd_fU) *fF;
              FLOAT fV = tvd1.tvd_fV - (tvd1.tvd_fV - tvd0.tvd_fV) *fF;
              tvdClipped.tvd_pv2.pv2_fUoK = fU * tvdClipped.tvd_pv2.pv2_f1oK;
              tvdClipped.tvd_pv2.pv2_fVoK = fV * tvdClipped.tvd_pv2.pv2_f1oK;
            }
          // if first vertex is out (don't add it into clip array)
          } else {
            // if second vertex is in
            if( fd1>=0) {
              // add clipped vertex at entry
              TransformedVertexData &tvdClipped = ptvdDst[ctvxDst];
              ctvxDst++;
              FLOAT fF = fd0/(fd0-fd1);
              tvdClipped.tvd_fX = tvd0.tvd_fX - (tvd0.tvd_fX - tvd1.tvd_fX) *fF;
              tvdClipped.tvd_fY = tvd0.tvd_fY - (tvd0.tvd_fY - tvd1.tvd_fY) *fF;
              tvdClipped.tvd_fZ = fBackClipDistance;
              tvdClipped.tvd_pv2.pv2_f1oK = fDepthBufferFactor * f1oBackClipDistance;
              FLOAT fU = tvd0.tvd_fU - (tvd0.tvd_fU - tvd1.tvd_fU) *fF;
              FLOAT fV = tvd0.tvd_fV - (tvd0.tvd_fV - tvd1.tvd_fV) *fF;
              tvdClipped.tvd_pv2.pv2_fUoK = fU * tvdClipped.tvd_pv2.pv2_f1oK;
              tvdClipped.tvd_pv2.pv2_fVoK = fV * tvdClipped.tvd_pv2.pv2_f1oK;
            }
          }
          // proceed to next vertex in list (i.e. new pair of vertices)
          ivx0=ivx1;
          ivx1++;
        }}
        // swap buffers
        Swap( ptvdSrc, ptvdDst);
        Swap( ctvxSrc, ctvxDst);
      }

      // for each vertex
      {for( INDEX ivx=0; ivx<ctvxSrc; ivx++)
      {
        // calculate projection
        TransformedVertexData &tvd = ptvdSrc[ivx];
        if( bPerspective) {
          const FLOAT f1oZ = 1.0f / tvd.tvd_fZ;
          tvd.tvd_pv2.pv2_fI = fCenterI + tvd.tvd_fX*fRatioI *f1oZ;
          tvd.tvd_pv2.pv2_fJ = fCenterJ - tvd.tvd_fY*fRatioJ *f1oZ;
        } else {
          tvd.tvd_pv2.pv2_fI = fCenterI + tvd.tvd_fX*fZoomI + tvd.tvd_fZ*fStepI;
          tvd.tvd_pv2.pv2_fJ = fCenterJ - tvd.tvd_fY*fZoomJ - tvd.tvd_fZ*fStepJ;
        }
      }}

      // clip polygon against left edge
      ctvxDst=0;
      ivx0=ctvxSrc-1;
      ivx1=0;
      {for( INDEX ivx=0; ivx<ctvxSrc; ivx++)
      {
        PolyVertex2D &pv20 = ptvdSrc[ivx0].tvd_pv2;
        PolyVertex2D &pv21 = ptvdSrc[ivx1].tvd_pv2;
        FLOAT fd0 = pv20.pv2_fI-0;
        FLOAT fd1 = pv21.pv2_fI-0;
        // if first vertex is in
        if( fd0>=0) {
          // add it to clip array
          ptvdDst[ctvxDst].tvd_pv2 = pv20;
          ctvxDst++;
          // if second vertex is out
          if( fd1<0) {
            PolyVertex2D &pv2Clipped = ptvdDst[ctvxDst].tvd_pv2;
            ctvxDst++;
            FLOAT fF = fd1/(fd1-fd0);
            pv2Clipped.pv2_fI = 0;
            pv2Clipped.pv2_fJ = pv21.pv2_fJ - (pv21.pv2_fJ - pv20.pv2_fJ) *fF;
            pv2Clipped.pv2_f1oK = pv21.pv2_f1oK - (pv21.pv2_f1oK - pv20.pv2_f1oK) *fF;
            pv2Clipped.pv2_fUoK = pv21.pv2_fUoK - (pv21.pv2_fUoK - pv20.pv2_fUoK) *fF;
            pv2Clipped.pv2_fVoK = pv21.pv2_fVoK - (pv21.pv2_fVoK - pv20.pv2_fVoK) *fF;
          }
        // if first vertex is out (don't add it into clip array)
        } else {
          // if second vertex is in
          if( fd1>=0) {
            // add clipped vertex at entry
            PolyVertex2D &pv2Clipped = ptvdDst[ctvxDst].tvd_pv2;
            ctvxDst++;
            FLOAT fF = fd0/(fd0-fd1);
            pv2Clipped.pv2_fI = 0;
            pv2Clipped.pv2_fJ = pv20.pv2_fJ - (pv20.pv2_fJ - pv21.pv2_fJ)*fF;
            pv2Clipped.pv2_f1oK = pv20.pv2_f1oK - (pv20.pv2_f1oK - pv21.pv2_f1oK) *fF;
            pv2Clipped.pv2_fUoK = pv20.pv2_fUoK - (pv20.pv2_fUoK - pv21.pv2_fUoK) *fF;
            pv2Clipped.pv2_fVoK = pv20.pv2_fVoK - (pv20.pv2_fVoK - pv21.pv2_fVoK) *fF;
          }
        }
        // proceed to next vertex in list (i.e. new pair of vertices)
        ivx0=ivx1;
        ivx1++;
      }}
      // swap buffers
      Swap( ptvdSrc, ptvdDst);
      Swap( ctvxSrc, ctvxDst);

      // clip polygon against right edge
      ctvxDst=0;
      ivx0=ctvxSrc-1;
      ivx1=0;
      {for( INDEX ivx=0; ivx<ctvxSrc; ivx++)
      {
        PolyVertex2D &pv20 = ptvdSrc[ivx0].tvd_pv2;
        PolyVertex2D &pv21 = ptvdSrc[ivx1].tvd_pv2;
        FLOAT fd0 = _slMaskWidth - pv20.pv2_fI;
        FLOAT fd1 = _slMaskWidth - pv21.pv2_fI;
        // if first vertex is in
        if( fd0>=0) {
          // add it to clip array
          ptvdDst[ctvxDst].tvd_pv2 = pv20;
          ctvxDst++;
          // if second vertex is out
          if( fd1<0) {
            PolyVertex2D &pv2Clipped = ptvdDst[ctvxDst].tvd_pv2;
            ctvxDst++;
            FLOAT fF = fd1/(fd1-fd0);
            pv2Clipped.pv2_fI = _slMaskWidth;
            pv2Clipped.pv2_fJ = pv21.pv2_fJ - (pv21.pv2_fJ - pv20.pv2_fJ)*fF;
            pv2Clipped.pv2_f1oK = pv21.pv2_f1oK - (pv21.pv2_f1oK - pv20.pv2_f1oK) *fF;
            pv2Clipped.pv2_fUoK = pv21.pv2_fUoK - (pv21.pv2_fUoK - pv20.pv2_fUoK) *fF;
            pv2Clipped.pv2_fVoK = pv21.pv2_fVoK - (pv21.pv2_fVoK - pv20.pv2_fVoK) *fF;
          }
        // if first vertex is out (don't add it into clip array)
        } else {
          // if second vertex is in
          if( fd1>=0) {
            // add clipped vertex at entry
            PolyVertex2D &pv2Clipped = ptvdDst[ctvxDst].tvd_pv2;
            ctvxDst++;
            FLOAT fF = fd0/(fd0-fd1);
            pv2Clipped.pv2_fI = _slMaskWidth;
            pv2Clipped.pv2_fJ = pv20.pv2_fJ - (pv20.pv2_fJ - pv21.pv2_fJ)*fF;
            pv2Clipped.pv2_f1oK = pv20.pv2_f1oK - (pv20.pv2_f1oK - pv21.pv2_f1oK) *fF;
            pv2Clipped.pv2_fUoK = pv20.pv2_fUoK - (pv20.pv2_fUoK - pv21.pv2_fUoK) *fF;
            pv2Clipped.pv2_fVoK = pv20.pv2_fVoK - (pv20.pv2_fVoK - pv21.pv2_fVoK) *fF;
          }
        }
        // proceed to next vertex in list (i.e. new pair of vertices)
        ivx0=ivx1;
        ivx1++;
      }}
      // swap buffers
      Swap( ptvdSrc, ptvdDst);
      Swap( ctvxSrc, ctvxDst);

      // draw all triangles in clipped polygon as a triangle fan, with clipping
      PolyVertex2D &pvx0 = ptvdSrc[0].tvd_pv2;
      {for( INDEX ivx=1; ivx<ctvxSrc-1; ivx++) {
        PolyVertex2D &pvx1 = ptvdSrc[ivx+0].tvd_pv2;
        PolyVertex2D &pvx2 = ptvdSrc[ivx+1].tvd_pv2;
        DrawTriangle_Mask( _pubMask, _slMaskWidth, _slMaskHeight, &pvx0, &pvx1, &pvx2, TRUE);
      }}
    }

    // not clipped - just draw!
    else {
      DrawTriangle_Mask( _pubMask, _slMaskWidth, _slMaskHeight,
                         &tvd[0].tvd_pv2, &tvd[1].tvd_pv2, &tvd[2].tvd_pv2, TRUE);
    }
  }
}
Example #15
0
// read depth buffer and update visibility flag of depth points
static void UpdateDepthPointsVisibility( const CDrawPort *pdp, const INDEX iMirrorLevel,
                                         DepthInfo *pdi, const INDEX ctCount)
{
  const GfxAPIType eAPI = _pGfx->gl_eCurrentAPI;
  ASSERT(GfxValidApi(eAPI));
  ASSERT( pdp!=NULL && ctCount>0);
  const CRaster *pra = pdp->dp_Raster;

  // OpenGL
  if( eAPI==GAT_OGL)
  { 
    _sfStats.StartTimer(CStatForm::STI_GFXAPI);
    FLOAT fPointOoK;
    // for each stored point
    for( INDEX idi=0; idi<ctCount; idi++) {
      DepthInfo &di = pdi[idi];
      // skip if not in required mirror level or was already checked in this iteration
      if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest) continue;
      const PIX pixJ = pra->ra_Height-1 - di.di_pixJ; // OpenGL has Y-inversed buffer!
      pglReadPixels( di.di_pixI, pixJ, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &fPointOoK);
      OGL_CHECKERROR;
      // it is visible if there is nothing nearer in z-buffer already
      di.di_bVisible = (di.di_fOoK<fPointOoK);
    }
    // done
    _sfStats.StopTimer(CStatForm::STI_GFXAPI);
    return;
  }

  // Direct3D
#ifdef SE1_D3D
  if( eAPI==GAT_D3D)
  {
    _sfStats.StartTimer(CStatForm::STI_GFXAPI);
    // ok, this will get really complicated ...
    // We'll have to do it thru back buffer because darn DX8 won't let us have values from z-buffer;
    // Anyway, we'll lock backbuffer, read color from the lens location and try to write little triangle there
    // with slightly modified color. Then we'll readout that color and see if triangle passes z-test. Voila! :)
    // P.S. To avoid lock-modify-lock, we need to batch all the locks in one. Uhhhh ... :(
    COLOR col;
    INDEX idi;
    SLONG slColSize;
    HRESULT hr;
    D3DLOCKED_RECT rectLocked;
    D3DSURFACE_DESC surfDesc;
    LPDIRECT3DSURFACE8 pBackBuffer;
    // fetch back buffer (different for full screen and windowed mode)
    const BOOL bFullScreen = _pGfx->gl_ulFlags & GLF_FULLSCREEN;
    if( bFullScreen) {
      hr = _pGfx->gl_pd3dDevice->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
    } else {
      hr = pra->ra_pvpViewPort->vp_pSwapChain->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
    }
    // what, cannot get a back buffer?
    if( hr!=D3D_OK) { 
      // to hell with it all
      _sfStats.StopTimer(CStatForm::STI_GFXAPI);
      return;
    }
    // keep format of back-buffer
    pBackBuffer->GetDesc(&surfDesc);
    const D3DFORMAT d3dfBack = surfDesc.Format;
    
    // prepare array that'll back-buffer colors from depth point locations
    _acolDelayed.Push(ctCount);
    // store all colors
    for( idi=0; idi<ctCount; idi++) {
      DepthInfo &di = pdi[idi];
      // skip if not in required mirror level or was already checked in this iteration
      if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest) continue;
      // fetch pixel
      _acolDelayed[idi] = 0;
      const RECT rectToLock = { di.di_pixI, di.di_pixJ, di.di_pixI+1, di.di_pixJ+1 };
      hr = pBackBuffer->LockRect( &rectLocked, &rectToLock, D3DLOCK_READONLY);
      if( hr!=D3D_OK) continue; // skip if lock didn't make it
      // read, convert and store original color
      _acolDelayed[idi] = UnpackColor_D3D( (UBYTE*)rectLocked.pBits, d3dfBack, slColSize) | CT_OPAQUE;
      pBackBuffer->UnlockRect();
    }

    // prepare to draw little triangles there with slightly adjusted colors
    _sfStats.StopTimer(CStatForm::STI_GFXAPI);
    gfxEnableDepthTest();
    gfxDisableDepthWrite();
    gfxDisableBlend();
    gfxDisableAlphaTest();
    gfxDisableTexture();
    _sfStats.StartTimer(CStatForm::STI_GFXAPI);
    // prepare array and shader
    _avtxDelayed.Push(ctCount*3);
    d3dSetVertexShader(D3DFVF_CTVERTEX);

    // draw one trianle around each depth point
    INDEX ctVertex = 0;
    for( idi=0; idi<ctCount; idi++) {
      DepthInfo &di = pdi[idi];
      col = _acolDelayed[idi];
      // skip if not in required mirror level or was already checked in this iteration, or wasn't fetched at all
      if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest || col==0) continue;
      const ULONG d3dCol = rgba2argb(col^0x20103000);
      const PIX pixI = di.di_pixI - pdp->dp_MinI; // convert raster loc to drawport loc
      const PIX pixJ = di.di_pixJ - pdp->dp_MinJ;
      // batch it and advance to next triangle
      CTVERTEX &vtx0 = _avtxDelayed[ctVertex++];
      CTVERTEX &vtx1 = _avtxDelayed[ctVertex++];
      CTVERTEX &vtx2 = _avtxDelayed[ctVertex++];
      vtx0.fX=pixI;   vtx0.fY=pixJ-2; vtx0.fZ=di.di_fOoK; vtx0.ulColor=d3dCol; vtx0.fU=vtx0.fV=0;
      vtx1.fX=pixI-2; vtx1.fY=pixJ+2; vtx1.fZ=di.di_fOoK; vtx1.ulColor=d3dCol; vtx1.fU=vtx0.fV=0;
      vtx2.fX=pixI+2; vtx2.fY=pixJ;   vtx2.fZ=di.di_fOoK; vtx2.ulColor=d3dCol; vtx2.fU=vtx0.fV=0;
    }
    // draw a bunch
    hr = _pGfx->gl_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, ctVertex/3, &_avtxDelayed[0], sizeof(CTVERTEX));
    D3D_CHECKERROR(hr);

    // readout colors again and compare to old ones
    for( idi=0; idi<ctCount; idi++) {
      DepthInfo &di = pdi[idi];
      col = _acolDelayed[idi];
      // skip if not in required mirror level or was already checked in this iteration, or wasn't fetched at all
      if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest || col==0) continue;
      // fetch pixel
      const RECT rectToLock = { di.di_pixI, di.di_pixJ, di.di_pixI+1, di.di_pixJ+1 };
      hr = pBackBuffer->LockRect( &rectLocked, &rectToLock, D3DLOCK_READONLY);
      if( hr!=D3D_OK) continue; // skip if lock didn't make it
      // read new color
      const COLOR colNew = UnpackColor_D3D( (UBYTE*)rectLocked.pBits, d3dfBack, slColSize) | CT_OPAQUE;
      pBackBuffer->UnlockRect();
      // if we managed to write adjusted color, point is visible!
      di.di_bVisible = (col!=colNew);
    }
    // phew, done! :)
    D3DRELEASE( pBackBuffer, TRUE);
    _acolDelayed.PopAll();
    _avtxDelayed.PopAll();
    _sfStats.StopTimer(CStatForm::STI_GFXAPI);
    return;
  }
#endif // SE1_D3D
}
Example #16
0
// setup CRenderModel class for rendering one model and eventually it's shadow(s)
void CModelObject::SetupModelRendering( CRenderModel &rm)
{
  _sfStats.IncrementCounter( CStatForm::SCI_MODELS);
  _pfModelProfile.StartTimer( CModelProfile::PTI_INITMODELRENDERING);
  _pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_INITMODELRENDERING);

  // get model's data and lerp info
  rm.rm_pmdModelData = (CModelData*)GetData();
  GetFrame( rm.rm_iFrame0, rm.rm_iFrame1, rm.rm_fRatio);
  const INDEX ctVertices = rm.rm_pmdModelData->md_VerticesCt;
  if( rm.rm_pmdModelData->md_Flags & MF_COMPRESSED_16BIT) {
    // set pFrame to point to last and next frames' vertices
    rm.rm_pFrame16_0 = &rm.rm_pmdModelData->md_FrameVertices16[rm.rm_iFrame0 *ctVertices];
    rm.rm_pFrame16_1 = &rm.rm_pmdModelData->md_FrameVertices16[rm.rm_iFrame1 *ctVertices];
  } else {
    // set pFrame to point to last and next frames' vertices
    rm.rm_pFrame8_0 = &rm.rm_pmdModelData->md_FrameVertices8[rm.rm_iFrame0 *ctVertices];
    rm.rm_pFrame8_1 = &rm.rm_pmdModelData->md_FrameVertices8[rm.rm_iFrame1 *ctVertices];
  }

  // obtain current rendering preferences
  rm.rm_rtRenderType = _mrpModelRenderPrefs.GetRenderType();
  // remember blending color
  rm.rm_colBlend = MulColors( rm.rm_colBlend, mo_colBlendColor);

  // get decompression/stretch factors
  FLOAT3D &vDataStretch = rm.rm_pmdModelData->md_Stretch;
  rm.rm_vStretch(1) = vDataStretch(1) * mo_Stretch(1);
  rm.rm_vStretch(2) = vDataStretch(2) * mo_Stretch(2);
  rm.rm_vStretch(3) = vDataStretch(3) * mo_Stretch(3);
  rm.rm_vOffset     = rm.rm_pmdModelData->md_vCompressedCenter;
  // check if object is inverted (in mirror)
  BOOL bXInverted = rm.rm_vStretch(1) < 0;
  BOOL bYInverted = rm.rm_vStretch(2) < 0;
  BOOL bZInverted = rm.rm_vStretch(3) < 0;
  rm.rm_ulFlags &= ~RMF_INVERTED;
  if( bXInverted != bYInverted != bZInverted != _aprProjection->pr_bInverted) rm.rm_ulFlags |= RMF_INVERTED;

  // prepare projections
  _pfModelProfile.StartTimer( CModelProfile::PTI_INITPROJECTION);
  _pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_INITPROJECTION);
  PrepareView(rm);
  _pfModelProfile.StopTimer( CModelProfile::PTI_INITPROJECTION);

  // get mip factor from projection (if needed)
  if( (INDEX&)rm.rm_fDistanceFactor==12345678) {
    FLOAT3D vObjectAbs;
    _aprProjection->PreClip( rm.rm_vObjectPosition, vObjectAbs);
    rm.rm_fDistanceFactor = _aprProjection->MipFactor( Min(vObjectAbs(3), 0.0f));
  }
  // adjust mip factor in case of dynamic stretch factor
  if( mo_Stretch != FLOAT3D(1,1,1)) {
    rm.rm_fMipFactor = rm.rm_fDistanceFactor - Log2( Max(mo_Stretch(1),Max(mo_Stretch(2),mo_Stretch(3))));
  } else {
    rm.rm_fMipFactor = rm.rm_fDistanceFactor;
  }
  // adjust mip factor by custom settings
  rm.rm_fMipFactor = rm.rm_fMipFactor*mdl_fLODMul +mdl_fLODAdd;

  // get current mip model using mip factor
  rm.rm_iMipLevel = GetMipModel( rm.rm_fMipFactor);
  mo_iLastRenderMipLevel = rm.rm_iMipLevel;
  // get current vertices mask
  rm.rm_pmmiMip = &rm.rm_pmdModelData->md_MipInfos[rm.rm_iMipLevel];

  // don't allow any shading, if shading is turned off
  if( rm.rm_rtRenderType & RT_SHADING_NONE) {
    rm.rm_colAmbient = C_WHITE|CT_OPAQUE;
    rm.rm_colLight   = C_BLACK;
  }

  // calculate light vector as seen from model, so that vertex normals
  // do not need to be transformed for lighting calculations
  FLOAT fLightDirection=(rm.rm_vLightDirection).Length();
  if( fLightDirection>0.001f) {
    rm.rm_vLightDirection /= fLightDirection;
  } else {
    rm.rm_vLightDirection = FLOAT3D(0,0,0);
  }
  rm.rm_vLightObj = rm.rm_vLightDirection * !rm.rm_mObjectRotation;

  // precalculate rendering data if needed
  extern void PrepareModelForRendering( CModelData &md);
  PrepareModelForRendering( *rm.rm_pmdModelData);

  // done with setup if viewing from this model
  if( rm.rm_ulFlags&RMF_SPECTATOR) {
    _pfModelProfile.StopTimer( CModelProfile::PTI_INITMODELRENDERING);
    return;
  }

  _pfModelProfile.StartTimer( CModelProfile::PTI_INITATTACHMENTS);
  // for each attachment on this model object
  FOREACHINLIST( CAttachmentModelObject, amo_lnInMain, mo_lhAttachments, itamo) {
    _pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_INITATTACHMENTS);
    CAttachmentModelObject *pamo = itamo;
    // create new render model structure
    pamo->amo_prm = &_armRenderModels.Push();
    const BOOL bVisible = CreateAttachment( rm, *pamo);
    if( !bVisible) { // skip if not visible
      pamo->amo_prm = NULL;
      _armRenderModels.Pop();
      continue;
    } // prepare if visible
    _pfModelProfile.StopTimer( CModelProfile::PTI_INITMODELRENDERING);
    _pfModelProfile.StopTimer( CModelProfile::PTI_INITATTACHMENTS);
    pamo->amo_moModelObject.SetupModelRendering( *pamo->amo_prm);
    _pfModelProfile.StartTimer( CModelProfile::PTI_INITATTACHMENTS);
    _pfModelProfile.StartTimer( CModelProfile::PTI_INITMODELRENDERING);
  }
Example #17
0
// Calculate lightning for given model
void shaCalculateLight(void)
{
  // if full bright
  if(shaGetFlags()&BASE_FULL_BRIGHT) {
    GFXColor colLight = _colConstant;
    GFXColor colAmbient;
    GFXColor colConstant;
    // is over brightning enabled
    if(shaOverBrightningEnabled()) {
      colAmbient = 0x7F7F7FFF;
    } else {
      colAmbient = 0xFFFFFFFF;
    }
    colConstant.MultiplyRGBA(colLight,colAmbient);
    shaSetConstantColor(ByteSwap(colConstant.abgr));
    // no vertex colors
    return;
  }

  ASSERT(_paNormals!=NULL);
  _acolVtxColors.PopAll();
  _acolVtxColors.Push(_ctVertices);

  GFXColor colModel   = (GFXColor)_colModel;   // Model color
  GFXColor &colAmbient = (GFXColor)_colAmbient; // Ambient color
  GFXColor &colLight   = (GFXColor)_colLight;   // Light color
  GFXColor &colSurface = (GFXColor)_colConstant; // shader color

  colModel.MultiplyRGBA(colModel,colSurface);

  UBYTE ubColShift = 8;
  SLONG slar = colAmbient.r;
  SLONG slag = colAmbient.g;
  SLONG slab = colAmbient.b;

  if(shaOverBrightningEnabled()) {
    slar = ClampUp(slar,127L);
    slag = ClampUp(slag,127L);
    slab = ClampUp(slab,127L);
    ubColShift = 8;
  } else {
    slar*=2;
    slag*=2;
    slab*=2;
    ubColShift = 7;
  }

  // for each vertex color
  for(INDEX ivx=0;ivx<_ctVertices;ivx++) {
    // calculate vertex light
    FLOAT3D &vNorm = FLOAT3D(_paNormals[ivx].nx,_paNormals[ivx].ny,_paNormals[ivx].nz);
    FLOAT fDot = vNorm % _vLightDir;
    fDot = Clamp(fDot,0.0f,1.0f);
    SLONG slDot = NormFloatToByte(fDot);

    _acolVtxColors[ivx].r = ClampUp(colModel.r * (slar + ((colLight.r * slDot)>>ubColShift))>>8,255L);
    _acolVtxColors[ivx].g = ClampUp(colModel.g * (slag + ((colLight.g * slDot)>>ubColShift))>>8,255L);
    _acolVtxColors[ivx].b = ClampUp(colModel.b * (slab + ((colLight.b * slDot)>>ubColShift))>>8,255L);
    _acolVtxColors[ivx].a = colModel.a;//slDot;
  }
  // Set current vertex color array 
  _pcolVtxColors = &_acolVtxColors[0];
}
void ShowSelectionInternal(CTerrain *ptrTerrain, Rect &rcExtract, CTextureData *ptdBrush, GFXColor colSelection, FLOAT fStrenght, SelectionFill sfFill)
{
  ASSERT(ptrTerrain!=NULL);
  ASSERT(ptdBrush!=NULL);

  Rect rcSelection;
  FLOATaabbox3D bboxSelection;
  // Clamp rect used for extraction
  rcSelection.rc_iLeft   = Clamp(rcExtract.rc_iLeft   , 0, ptrTerrain->tr_pixHeightMapWidth);
  rcSelection.rc_iTop    = Clamp(rcExtract.rc_iTop    , 0, ptrTerrain->tr_pixHeightMapHeight);
  rcSelection.rc_iRight  = Clamp(rcExtract.rc_iRight  , 0, ptrTerrain->tr_pixHeightMapWidth);
  rcSelection.rc_iBottom = Clamp(rcExtract.rc_iBottom , 0, ptrTerrain->tr_pixHeightMapHeight);

  // Prepare box for vertex selection
  bboxSelection    = FLOAT3D(rcSelection.rc_iLeft,  0, rcSelection.rc_iTop);
  bboxSelection   |= FLOAT3D(rcSelection.rc_iRight, 0, rcSelection.rc_iBottom);

  // Stretch selection box
  bboxSelection.minvect(1) *= ptrTerrain->tr_vStretch(1);
  bboxSelection.minvect(3) *= ptrTerrain->tr_vStretch(3);
  bboxSelection.maxvect(1) *= ptrTerrain->tr_vStretch(1);
  bboxSelection.maxvect(3) *= ptrTerrain->tr_vStretch(3);

  // Set selection box height
  FLOATaabbox3D bboxAllTerrain;
  ptrTerrain->GetAllTerrainBBox(bboxAllTerrain);
  bboxSelection.minvect(2) = bboxAllTerrain.minvect(2);
  bboxSelection.maxvect(2) = bboxAllTerrain.maxvect(2);

  GFXVertex *pavVertices;
  INDEX     *paiIndices;
  INDEX      ctVertices;
  INDEX      ctIndices;
  
  // Extract vertices in selection rect
  ExtractVerticesInRect(ptrTerrain, rcSelection, &pavVertices, &paiIndices, ctVertices, ctIndices);

  if(ctVertices!=rcSelection.Width()*rcSelection.Height()) {
    ASSERT(FALSE);
    return;
  }

  // if no vertices
  if(ctVertices==0) {
    return;
  }

  // Prepare vertex colors for selection preview
  PIX pixWidth  = rcSelection.Width();
  PIX pixHeight = rcSelection.Height();
  INDEX iStepX  = ptdBrush->GetWidth() - pixWidth;
  INDEX iFirst  = 0;
  if(rcExtract.rc_iTop<0) {
    iFirst += -rcExtract.rc_iTop*ptdBrush->GetWidth();
  }
  if(rcExtract.rc_iLeft<0) {
    iFirst += -rcExtract.rc_iLeft;
  }

  _aiExtColors.Push(ctVertices);
  GFXColor *pacolColor = (GFXColor*)&_aiExtColors[0];
  GFXColor *pacolBrush = (GFXColor*)&ptdBrush->td_pulFrames[iFirst];

  // Fill vertex colors for selection preview
  SLONG slStrength = (SLONG) (Clamp(Abs(fStrenght),0.0f,1.0f) * 256.0f);
  // for each row
  for(INDEX iy=0;iy<pixHeight;iy++) {
    // for each col
    for(INDEX ix=0;ix<pixWidth;ix++) {
      pacolColor->ul.abgr = colSelection.ul.abgr;
      pacolColor->ub.a    = (pacolBrush->ub.r*slStrength)>>8;
      pacolColor++;
      pacolBrush++;
    }
    pacolBrush+=iStepX;
  }

  // Render selected polygons for selection preview
  if(sfFill == SF_WIREFRAME) {
    gfxPolygonMode(GFX_LINE);
    gfxEnableDepthBias();
  }

  if(sfFill != SF_POINTS) {
    // Draw selection
    gfxDisableTexture();
    gfxDisableAlphaTest();
    gfxEnableBlend();
    gfxBlendFunc(GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
    gfxSetVertexArray(pavVertices,ctVertices);
    gfxSetColorArray(&_aiExtColors[0]);
    gfxLockArrays();
    gfxDrawElements(ctIndices,paiIndices);
    gfxUnlockArrays();
    gfxDisableBlend();
  }

  if(sfFill == SF_WIREFRAME) {
    gfxDisableDepthBias();
    gfxPolygonMode(GFX_FILL);
  }

  if(sfFill == SF_POINTS) {
    DrawSelectedVertices(pavVertices,&_aiExtColors[0],ctVertices);
  }
}
static void RenderFogLayer(INDEX itt)
{
  FLOATmatrix3D &mViewer = _aprProjection->pr_ViewerRotationMatrix;
  FLOAT3D vObjPosition = _ptrTerrain->tr_penEntity->en_plPlacement.pl_PositionVector;

  // get viewer -z in object space
  _vFViewerObj = FLOAT3D(0,0,-1) * !_mObjectToView;
  // get fog direction in object space
  _vHDirObj = _fog_vHDirAbs * !(!mViewer*_mObjectToView);
  // get viewer offset
  _fFogAddZ  = _vViewer(1) * (vObjPosition(1) - _aprProjection->pr_vViewerPosition(1));
  _fFogAddZ += _vViewer(2) * (vObjPosition(2) - _aprProjection->pr_vViewerPosition(2));
  _fFogAddZ += _vViewer(3) * (vObjPosition(3) - _aprProjection->pr_vViewerPosition(3));
  // get fog offset
  _fFogAddH = (_fog_vHDirAbs % vObjPosition) + _fog_fp.fp_fH3;

  GFXVertex *pvVtx;
  INDEX     *piIndices;
  INDEX ctVertices;
  INDEX ctIndices;
  // if this is tile 
  if(itt>=0) {
    CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
    pvVtx      = &tt.GetVertices()[0];
    piIndices  = &tt.GetIndices()[0];
    ctVertices = tt.GetVertices().Count();
    ctIndices  = tt.GetIndices().Count();
  // else this are batched tiles
  } else {
    pvVtx      = &_avDelayedVertices[0];
    piIndices  = &_aiDelayedIndices[0];
    ctVertices = _avDelayedVertices.Count();
    ctIndices  = _aiDelayedIndices.Count();
  }

  GFXTexCoord *pfFogTC  = _atcHaze.Push(ctVertices);
  GFXColor    *pcolFog  = _acolHaze.Push(ctVertices);

  const COLOR colF = AdjustColor( _fog_fp.fp_colColor, _slTexHueShift, _slTexSaturation);
  GFXColor colFog(colF);

  // for each vertex in tile
  for(INDEX ivx=0;ivx<ctVertices;ivx++) {
    GetFogMapInVertex(pvVtx[ivx],pfFogTC[ivx]);
    pcolFog[ivx] = colFog;
  }

  // render fog layer
  gfxDepthFunc(GFX_EQUAL);
  gfxSetTextureWrapping( GFX_CLAMP, GFX_CLAMP);
  gfxSetTexture( _fog_ulTexture, _fog_tpLocal);
  gfxSetTexCoordArray(pfFogTC, FALSE);
  gfxSetColorArray(pcolFog);
  gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
  gfxEnableBlend();
  gfxDisableAlphaTest();
  gfxDrawElements(ctIndices,piIndices);
  gfxDepthFunc(GFX_LESS_EQUAL);

  _atcHaze.PopAll();
  _acolHaze.PopAll();
}
void PrepareSmothVertices(INDEX itt)
{
  CTerrainTile &tt  = _ptrTerrain->tr_attTiles[itt];
  const INDEX ctVertices = tt.GetVertices().Count();
  const FLOAT &fLerpFactor = tt.tt_fLodLerpFactor;

  // Allocate memory for all vertices
  _avLerpedVerices.PopAll();
  _avLerpedVerices.Push(ctVertices);
  
  // Get pointers to src and dst vertex arrays
  GFXVertex *pavSrcFirst = &tt.GetVertices()[0];
  GFXVertex *pavDstFirst = &_avLerpedVerices[0];
  GFXVertex *pavSrc = &pavSrcFirst[0];
  GFXVertex *pavDst = &pavDstFirst[0];

  INDEX iFacing=0;
  // for each vertex column
  for(INDEX iy=0;iy<tt.tt_ctLodVtxY;iy++) {
    // for each vertex in row in even column step by 2
    for(INDEX ix=0;ix<tt.tt_ctLodVtxX-2;ix+=2) {
      // Copy first vertex
      pavDst[0] = pavSrc[0];
      // Second vertex is lerped between left and right vertices
      Lerp(pavDst[1],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
      // Increment vertex pointers
      pavDst+=2;
      pavSrc+=2;
    }
    // Copy last vertex in row
    pavDst[0] = pavSrc[0];
   
    // Increment vertex pointers and go to odd column 
    pavDst++;
    pavSrc++;
    iy++;

    // if this is not last row
    if(iy<tt.tt_ctLodVtxY) {
      // for each vertex in row in odd column step by 2
      for(INDEX ix=0;ix<tt.tt_ctLodVtxX-2;ix+=2) {
        // First vertex is lerped between top and bottom vertices
        Lerp(pavDst[0],pavSrc[0],pavSrc[-tt.tt_ctLodVtxX],pavSrc[tt.tt_ctLodVtxX],fLerpFactor);
        // is this odd vertex in row
        #pragma message(">> Fix this")
        if(((ix+iy)/2)%2) {
        // if(iFacing&1)
          // Second vertex (diagonal one) is lerped between topright and bottom left vertices
          Lerp(pavDst[1],pavSrc[1],pavSrc[-tt.tt_ctLodVtxX+2],pavSrc[tt.tt_ctLodVtxX],fLerpFactor);
        } else {
          // Second vertex (diagonal one) is lerped between topleft and bottom right vertices
          Lerp(pavDst[1],pavSrc[1],pavSrc[-tt.tt_ctLodVtxX],pavSrc[tt.tt_ctLodVtxX+2],fLerpFactor);
        }
        iFacing++;
        // Increment vertex pointers
        pavDst+=2;
        pavSrc+=2;
      }
      // Last vertex in row is lerped between top and bottom vertices (same as first in row)
      Lerp(pavDst[0],pavSrc[0],pavSrc[-tt.tt_ctLodVtxX],pavSrc[tt.tt_ctLodVtxX],fLerpFactor);
    }
    // Increment vertex pointers
    pavDst++;
    pavSrc++;
  }

  pavDst--;
  pavSrc--;
/*
  // Copy border vertices
  GFXVertex *pvBorderDst = pavDst;
  GFXVertex *pvBorderSrc = pavSrc;

  for(INDEX ivx=tt.tt_ctNonBorderVertices;ivx<ctVertices;ivx++) {
    //*pavDst++ = *pavSrc++;
    pvBorderDst[0] = pvBorderSrc[0];
    pvBorderDst++;
    pvBorderSrc++;
  }
*/
  // Lerp top border vertices
  const INDEX &iTopNeigbour = tt.tt_aiNeighbours[NB_TOP];
  // if top neighbour exists
  if(iTopNeigbour>=0) {
    CTerrainTile &ttTop = _ptrTerrain->tr_attTiles[iTopNeigbour];
    const FLOAT &fLerpFactor = ttTop.tt_fLodLerpFactor;
    // Get source vertex pointer in top neighbour (vertex in bottom left corner of top neighbour)
    const INDEX iSrcVtx = ttTop.tt_ctLodVtxX * (ttTop.tt_ctLodVtxY-1);
    GFXVertex *pavSrc = &ttTop.GetVertices()[iSrcVtx];
    // Calculate num of vertices that needs to be lerped
    const INDEX ctLerps = (ttTop.tt_ctLodVtxX-1)/2;

    // is top tile in same lod as this tile and has smaller or equal lerp factor
    if(tt.tt_iLod==ttTop.tt_iLod && fLerpFactor<=tt.tt_fLodLerpFactor) {
      // Get destination vertex pointer in this tile (first vertex in top left corner of this tile - first vertex in array)
      const INDEX iDstVtx = 0;
      GFXVertex *pavDst = &pavDstFirst[iDstVtx];

      // for each vertex in bottom row of top tile that needs to be lerped
      for(INDEX ivx=0;ivx<ctLerps;ivx++) {
        // First vertex is same as in top tile
        pavDst[0] = pavSrc[0];
        // Second vertex is lerped between left and right vertices
        Lerp(pavDst[1],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
        pavDst+=2;
        pavSrc+=2;
      }
    // is top tile in higher lod
    } else if(tt.tt_iLod>ttTop.tt_iLod) {
      const INDEX iVtxDiff = (ttTop.tt_ctLodVtxX-1) / (tt.tt_ctLodVtxX-1);
      // Get destination vertex pointer to copy vertices from top neighbour (first vertex in top left corner of this tile - first vertex in array)
      // Get destination vertex pointer to lerp vertices from top neighbour (first vertex added as additional top border vertex)
      const INDEX iDstCopyVtx = 0;
      const INDEX iDstLerpVtx = tt.tt_iFirstBorderVertex[NB_TOP];
      GFXVertex *pavDstCopy = &pavDstFirst[iDstCopyVtx];
      GFXVertex *pavDstLerp = &pavDstFirst[iDstLerpVtx];

      // if diference is in one lod
      if(iVtxDiff==2) {
        // for each vertex in bottom row of top tile that needs to be lerped
        for(INDEX ivx=0;ivx<ctLerps;ivx++) {
          // Copy src vertex in normal dst vertex array
          pavDstCopy[0] = pavSrc[0];
          // Lerp left and right src vertices in border dst vertex
          Lerp(pavDstLerp[0],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
          pavDstLerp++;
          pavDstCopy++;
          pavSrc+=2;
        }
      // diference is more than one lod
      } else {
        INDEX ctbv = tt.tt_ctBorderVertices[NB_TOP];
        INDEX ivxInQuad = 2; // This is 2 cos first and last non border vertex 
        // for each border vertex
        for(INDEX ivx=0;ivx<ctbv;ivx+=2) {
          // Lerp left and right src vertices in border dst vertex
          Lerp(pavDstLerp[0],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
          // if this border vertex is not last in quad
          if(ivxInQuad!=iVtxDiff) {
            // Copy second border vertex
            pavDstLerp[1] = pavSrc[2];
            pavDstLerp+=2;
            ivxInQuad+=2;
          // this is last border vertex
          } else {
            // Copy second non border vertex
            pavDstCopy[1] = pavSrc[2];
            pavDstCopy++;
            // since this wasn't border vertex, fix border vertex loop counter
            ctbv++;
            pavDstLerp++;
            ivxInQuad=2;
          }
          pavSrc+=2;
        }
      }
    }
  }

  // Lerp bottom border vertices
  const INDEX &iBottomNeigbour = tt.tt_aiNeighbours[NB_BOTTOM];
  // if bottom neighbour exists
  if(iBottomNeigbour>=0) {
    CTerrainTile &ttBottom = _ptrTerrain->tr_attTiles[iBottomNeigbour];
    const FLOAT &fLerpFactor = ttBottom.tt_fLodLerpFactor;
    // Get source vertex pointer in bottom neighbour (vertex in top left corner of bottom neighbour - first vertex in array) 
    const INDEX iSrcVtx = 0;
    GFXVertex *pavSrc = &ttBottom.GetVertices()[iSrcVtx];
    // Calculate num of vertices that needs to be lerped
    const INDEX ctLerps = (ttBottom.tt_ctLodVtxX-1)/2;

    // is bottom tile in same lod as this tile and has smaller lerp factor
    if(tt.tt_iLod==ttBottom.tt_iLod && fLerpFactor<tt.tt_fLodLerpFactor) {
      // Get destination vertex pointer in this tile (first vertex in bottom left corner of this tile)
      const INDEX iDstVtx = tt.tt_ctLodVtxX * (tt.tt_ctLodVtxY-1);
      GFXVertex *pavDst = &pavDstFirst[iDstVtx];

      // for each vertex in top row of bottom tile that needs to be lerped
      for(INDEX ivx=0;ivx<ctLerps;ivx++) {
        // First vertex is same as in bottom tile
        pavDst[0] = pavSrc[0];
        // Second vertex is lerped between left and right vertices
        Lerp(pavDst[1],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
        pavDst+=2;
        pavSrc+=2;
      }
    // is bottom tile in higher lod
    } else if(tt.tt_iLod>ttBottom.tt_iLod) {
      const INDEX iVtxDiff = (ttBottom.tt_ctLodVtxX-1) / (tt.tt_ctLodVtxX-1);
      // Get destination vertex pointer to copy vertices from bottom neighbour (first vertex in bottom left corner of this tile)
      // Get destination vertex pointer to lerp vertices from bottom neighbour (first vertex added as additional bottom border vertex)
      const INDEX iDstCopyVtx = tt.tt_ctLodVtxX * (tt.tt_ctLodVtxY-1);
      const INDEX iDstLerpVtx = tt.tt_iFirstBorderVertex[NB_BOTTOM];
      GFXVertex *pavDstCopy = &pavDstFirst[iDstCopyVtx];
      GFXVertex *pavDstLerp = &pavDstFirst[iDstLerpVtx];

      // if diference is in one lod
      if(iVtxDiff==2) {
        // for each vertex in top row of bottom tile that needs to be lerped
        for(INDEX ivx=0;ivx<ctLerps;ivx++) {
          // Copy src vertex in normal dst vertex array
          pavDstCopy[0] = pavSrc[0];
          // Lerp left and right src vertices in border dst vertex
          Lerp(pavDstLerp[0],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
          pavDstLerp++;
          pavDstCopy++;
          pavSrc+=2;
        }
      // diference is more than one lod
      } else {
        INDEX ctbv = tt.tt_ctBorderVertices[NB_BOTTOM];
        INDEX ivxInQuad = 2; // This is 2 cos first and last non border vertex 
        // for each border vertex
        for(INDEX ivx=0;ivx<ctbv;ivx+=2) {
          // Lerp left and right src vertices in border dst vertex
          Lerp(pavDstLerp[0],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
          // if this border vertex is not last in quad
          if(ivxInQuad!=iVtxDiff) {
            // Copy second border vertex
            pavDstLerp[1] = pavSrc[2];
            pavDstLerp+=2;
            ivxInQuad+=2;
          // this is last border vertex
          } else {
            // Copy second non border vertex
            pavDstCopy[1] = pavSrc[2];
            pavDstCopy++;
            // since this wasn't border vertex, fix border vertex loop counter
            ctbv++;
            pavDstLerp++;
            ivxInQuad=2;
          }
          pavSrc+=2;
        }
      }
    }
  }

  // Lerp left border vertices
  const INDEX &iLeftNeigbour = tt.tt_aiNeighbours[NB_LEFT];
  // if left neighbour exists
  if(iLeftNeigbour>=0) {
    CTerrainTile &ttLeft = _ptrTerrain->tr_attTiles[iLeftNeigbour];
    const FLOAT &fLerpFactor = ttLeft.tt_fLodLerpFactor;
    // Get source vertex pointer in left neighbour (vertex in top right corner of left neighbour) 
    const INDEX iSrcVtx = ttLeft.tt_ctLodVtxX-1;
    const INDEX iSrcStep = ttLeft.tt_ctLodVtxX;
    GFXVertex *pavSrc = &ttLeft.GetVertices()[iSrcVtx];
    // Calculate num of vertices that needs to be lerped
    const INDEX ctLerps = (ttLeft.tt_ctLodVtxX-1)/2;

    // is left tile in same lod as this tile and has smaller or equal lerp factor
    if(tt.tt_iLod==ttLeft.tt_iLod && fLerpFactor<=tt.tt_fLodLerpFactor) {
      // Get destination vertex pointer in this tile (first vertex in top left corner of this tile - first vertex in array)
      const INDEX iDstVtx = 0;
      const INDEX iDstStep = tt.tt_ctLodVtxX;
      GFXVertex *pavDst = &pavDstFirst[iDstVtx];

      // for each vertex in last column of left tile that needs to be lerped
      for(INDEX ivx=0;ivx<ctLerps;ivx++) {
        // First vertex is same as in left tile
        pavDst[0] = pavSrc[0];
        // Second vertex is lerped between top and bottom vertices
        Lerp(pavDst[iDstStep],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
        pavDst+=iDstStep*2;
        pavSrc+=iSrcStep*2;
      }
    // is left tile in higher lod
    } else if(tt.tt_iLod>ttLeft.tt_iLod) {
      const INDEX iVtxDiff = (ttLeft.tt_ctLodVtxX-1) / (tt.tt_ctLodVtxX-1);
      // Get destination vertex pointer to copy vertices from left neighbour (first vertex in top left corner of this tile - first vertex in array)
      // Get destination vertex pointer to lerp vertices from left neighbour (first vertex added as additional left border vertex)
      const INDEX iDstCopyVtx = 0;
      const INDEX iDstLerpVtx = tt.tt_iFirstBorderVertex[NB_LEFT];
      const INDEX iDstStep = tt.tt_ctLodVtxX;
      GFXVertex *pavDstCopy = &pavDstFirst[iDstCopyVtx];
      GFXVertex *pavDstLerp = &pavDstFirst[iDstLerpVtx];

      // if diference is in one lod
      if(iVtxDiff==2) {
        // for each vertex in last column of left tile that needs to be lerped
        for(INDEX ivx=0;ivx<ctLerps;ivx++) {
          // Copy src vertex in normal dst vertex array
          pavDstCopy[0] = pavSrc[0];
          // Lerp left and right src vertices in border dst vertex
          Lerp(pavDstLerp[0],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
          pavDstLerp++;
          pavDstCopy+=iDstStep;
          pavSrc+=iSrcStep*2;
        }
      // diference is more than one lod
      } else {
        INDEX ctbv = tt.tt_ctBorderVertices[NB_LEFT];
        INDEX ivxInQuad = 2; // This is 2 cos first and last non border vertex 
        // for each border vertex
        for(INDEX ivx=0;ivx<ctbv;ivx+=2) {
          // Lerp left and right src vertices in border dst vertex
          Lerp(pavDstLerp[0],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
          // if this border vertex is not last in quad
          if(ivxInQuad!=iVtxDiff) {
            // Copy second border vertex
            pavDstLerp[1] = pavSrc[iSrcStep*2];
            pavDstLerp+=2;
            ivxInQuad+=2;
          // this is last border vertex
          } else {
            // Copy second non border vertex
            pavDstCopy[iDstStep] = pavSrc[iSrcStep*2];
            pavDstCopy+=iDstStep;
            // since this wasn't border vertex, fix border vertex loop counter
            ctbv++;
            pavDstLerp++;
            ivxInQuad=2;
          }
          pavSrc+=iSrcStep*2;
        }
      }
    }
  }

  // Lerp right border vertices
  const INDEX &iRightNeigbour = tt.tt_aiNeighbours[NB_RIGHT];
  // if right neighbour exists
  if(iRightNeigbour>=0) {
    CTerrainTile &ttRight = _ptrTerrain->tr_attTiles[iRightNeigbour];
    const FLOAT &fLerpFactor = ttRight.tt_fLodLerpFactor;
    // Get source vertex pointer in right neighbour (vertex in top left corner of left neighbour - first vertex in array) 
    const INDEX iSrcVtx = 0;
    const INDEX iSrcStep = ttRight.tt_ctLodVtxX;
    GFXVertex *pavSrc = &ttRight.GetVertices()[iSrcVtx];
    // Calculate num of vertices that needs to be lerped
    const INDEX ctLerps = (ttRight.tt_ctLodVtxX-1)/2;

    // is right tile in same lod as this tile and has smaller lerp factor
    if(tt.tt_iLod==ttRight.tt_iLod && fLerpFactor<tt.tt_fLodLerpFactor) {
      // Get destination vertex pointer in this tile (first vertex in top right corner of this tile)
      INDEX iDstVtx = tt.tt_ctLodVtxX-1;
      INDEX iDstStep = tt.tt_ctLodVtxX;
      GFXVertex *pavDst = &pavDstFirst[iDstVtx];

      // for each vertex in first column of right tile that needs to be lerped
      for(INDEX ivx=0;ivx<ctLerps;ivx++) {
        // First vertex is same as in right tile
        pavDst[0] = pavSrc[0];
        // Second vertex is lerped between top and bottom vertices
        Lerp(pavDst[iDstStep],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
        pavDst+=iDstStep*2;
        pavSrc+=iSrcStep*2;
      }
    // is right tile in higher lod
    } else if(tt.tt_iLod>ttRight.tt_iLod) {
      const INDEX iVtxDiff = (ttRight.tt_ctLodVtxX-1) / (tt.tt_ctLodVtxX-1);
      // Get destination vertex pointer to copy vertices from right neighbour (first vertex in top right corner of this tile)
      // Get destination vertex pointer to lerp vertices from right neighbour (first vertex added as additional right border vertex)
      const INDEX iDstCopyVtx = tt.tt_ctLodVtxX-1;
      const INDEX iDstLerpVtx = tt.tt_iFirstBorderVertex[NB_RIGHT];
      const INDEX iDstStep = tt.tt_ctLodVtxX;
      GFXVertex *pavDstCopy = &pavDstFirst[iDstCopyVtx];
      GFXVertex *pavDstLerp = &pavDstFirst[iDstLerpVtx];

      // if diference is in one lod
      if(iVtxDiff==2) {
        // for each vertex in first column of right tile that needs to be lerped
        for(INDEX ivx=0;ivx<ctLerps;ivx++) {
          // Copy src vertex in normal dst vertex array
          pavDstCopy[0] = pavSrc[0];
          // Lerp left and right src vertices in border dst vertex
          Lerp(pavDstLerp[0],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
          pavDstLerp++;
          pavDstCopy+=iDstStep;
          pavSrc+=iSrcStep*2;
        }
      // diference is more than one lod
      } else {
        INDEX ctbv = tt.tt_ctBorderVertices[NB_RIGHT];
        INDEX ivxInQuad = 2; // This is 2 cos first and last non border vertex 
        // for each border vertex
        for(INDEX ivx=0;ivx<ctbv;ivx+=2) {
          // Lerp left and right src vertices in border dst vertex
          Lerp(pavDstLerp[0],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
          // if this border vertex is not last in quad
          if(ivxInQuad!=iVtxDiff) {
            // Copy second border vertex
            pavDstLerp[1] = pavSrc[iSrcStep*2];
            pavDstLerp+=2;
            ivxInQuad+=2;
          // this is last border vertex
          } else {
            // Copy second non border vertex
            pavDstCopy[iDstStep] = pavSrc[iSrcStep*2];
            pavDstCopy+=iDstStep;
            // since this wasn't border vertex, fix border vertex loop counter
            ctbv++;
            pavDstLerp++;
            ivxInQuad=2;
          }
          pavSrc+=iSrcStep*2;
        }
      }
    }
  }
}
void PrepareSmothVerticesOnTileLayer(INDEX iTerrainTile, INDEX iTileLayer)
{
  CTerrainTile &tt  = _ptrTerrain->tr_attTiles[iTerrainTile];
  CTerrainLayer &tl = _ptrTerrain->tr_atlLayers[iTileLayer];
  TileLayer &ttl    = tt.GetTileLayers()[iTileLayer];

  ASSERT(tt.tt_iLod==0);

  const INDEX ctVertices = ttl.tl_avVertices.Count();
  const FLOAT &fLerpFactor = tt.tt_fLodLerpFactor;

  // Allocate memory for all vertices
  _avLerpedTileLayerVertices.PopAll();
  _avLerpedTileLayerVertices.Push(ctVertices);
  
  // Get pointers to src and dst vertex arrays
  GFXVertex *pavSrcFirst = &ttl.tl_avVertices[0];
  GFXVertex *pavDstFirst = &_avLerpedTileLayerVertices[0];
  GFXVertex *pavSrc = &pavSrcFirst[0];
  GFXVertex *pavDst = &pavDstFirst[0];

  INDEX ctQuadsPerRow   = _ptrTerrain->tr_ctQuadsInTileRow;
  INDEX ctVerticesInRow = _ptrTerrain->tr_ctQuadsInTileRow*2;
  INDEX iFacing = 1;

  // Minimize popping on vertices using 4 quads, 2 from current row and 2 from next row in same tile
  for(INDEX iz=0;iz<ctQuadsPerRow;iz+=2) {
    for(INDEX ix=0;ix<ctQuadsPerRow;ix+=2) {
      // Get pointer for quads in next row
      GFXVertex *pavNRSrc = &pavSrc[ctVerticesInRow*2];
      GFXVertex *pavNRDst = &pavDst[ctVerticesInRow*2];

      pavDst[0]   = pavSrc[0];
      Lerp(pavDst[1],pavSrc[1],pavSrc[0],pavSrc[5],fLerpFactor);
      Lerp(pavDst[2],pavSrc[2],pavSrc[0],pavNRSrc[2],fLerpFactor);

      if(iFacing&1) {
        Lerp(pavDst[3],pavSrc[3],pavSrc[0],pavNRSrc[7],fLerpFactor);
      } else {
        Lerp(pavDst[3],pavSrc[3],pavSrc[5],pavNRSrc[2],fLerpFactor);
      }

      pavDst[4]   = pavDst[1];
      pavDst[5]   = pavSrc[5];
      pavDst[6]   = pavDst[3];
      Lerp(pavDst[7],pavSrc[7],pavSrc[5],pavNRSrc[7],fLerpFactor);
      pavNRDst[0] = pavDst[2];
      pavNRDst[1] = pavDst[3];
      pavNRDst[2] = pavNRSrc[2];
      Lerp(pavNRDst[3],pavNRSrc[3],pavNRSrc[2],pavNRSrc[7],fLerpFactor);
      pavNRDst[4] = pavDst[3];
      pavNRDst[5] = pavDst[7];
      pavNRDst[6] = pavNRDst[3];
      pavNRDst[7] = pavNRSrc[7];

      // Increment vertex pointers
      pavSrc+=8;
      pavDst+=8;
      iFacing++;
    }
    iFacing++;
    pavSrc+=ctVerticesInRow*2;
    pavDst+=ctVerticesInRow*2;
  }

  // Lerp top border
  INDEX iTopNeighbour = tt.tt_aiNeighbours[NB_TOP];
  // if top border exists
  if(iTopNeighbour>=0) {
    CTerrainTile &ttTop = _ptrTerrain->tr_attTiles[iTopNeighbour];
    const FLOAT fTopLerpFactor = ttTop.tt_fLodLerpFactor;
    // is top tile in highest lod and has smaller or equal lerp factor
    if(ttTop.tt_iLod==0 && fTopLerpFactor<=fLerpFactor) {
      TileLayer &ttl = ttTop.GetTileLayers()[iTileLayer];
      INDEX iFirstVertex = ctVerticesInRow*(ctVerticesInRow-2);
      GFXVertex *pavSrc = &ttl.tl_avVertices[iFirstVertex];
      GFXVertex *pavDst = &_avLerpedTileLayerVertices[0];
      // for each quad
      for(INDEX ix=0;ix<ctQuadsPerRow;ix+=2) {
        Lerp(pavDst[1],pavSrc[6],pavSrc[2],pavSrc[7],fTopLerpFactor);
        pavDst[4] = pavDst[1];
        pavSrc+=8;
        pavDst+=8;
      }
    }
  }

  // Lerp bottom border
  INDEX iBottomNeighbour = tt.tt_aiNeighbours[NB_BOTTOM];
  // if bottom border exists
  if(iBottomNeighbour>=0) {
    CTerrainTile &ttBottom = _ptrTerrain->tr_attTiles[iBottomNeighbour];
    const FLOAT fBottomLerpFactor = ttBottom.tt_fLodLerpFactor;
    // is bottom tile in highest lod and has smaller lerp factor
    if(ttBottom.tt_iLod==0 && fBottomLerpFactor<fLerpFactor) {
      TileLayer &ttl = ttBottom.GetTileLayers()[iTileLayer];
      INDEX iFirstVertex = ctVerticesInRow*(ctVerticesInRow-2);
      GFXVertex *pavSrc = &ttl.tl_avVertices[0];
      GFXVertex *pavDst = &_avLerpedTileLayerVertices[iFirstVertex];
      // for each quad
      for(INDEX ix=0;ix<ctQuadsPerRow;ix+=2) {
        Lerp(pavDst[3],pavSrc[1],pavSrc[0],pavSrc[5],fBottomLerpFactor);
        pavDst[6] = pavDst[3];
        pavSrc+=8;
        pavDst+=8;
      }
    }
  }

  // Lerp left border
  INDEX iLeftNeighbour = tt.tt_aiNeighbours[NB_LEFT];
  // if left neightbour exits
  if(iLeftNeighbour>=0) {
    CTerrainTile &ttLeft = _ptrTerrain->tr_attTiles[iLeftNeighbour];
    const FLOAT fLeftLerpFactor = ttLeft.tt_fLodLerpFactor;
    // is left tile in highest lod and has smaller or equal left factor
    if(ttLeft.tt_iLod==0 && fLeftLerpFactor<=fLerpFactor) {
      TileLayer &ttl = ttLeft.GetTileLayers()[iTileLayer];
      INDEX iFirstVertex = ctVerticesInRow*2-8;
      GFXVertex *pavSrc = &ttl.tl_avVertices[iFirstVertex];
      GFXVertex *pavDst = &_avLerpedTileLayerVertices[0];
      // for each quad
      for(INDEX ix=0;ix<ctQuadsPerRow;ix+=2) {
        GFXVertex *pavNRSrc = &pavSrc[ctVerticesInRow*2];
        GFXVertex *pavNRDst = &pavDst[ctVerticesInRow*2];

        Lerp(pavDst[2],pavSrc[7],pavSrc[5],pavNRSrc[7],fLeftLerpFactor);
        pavNRDst[0] = pavDst[2];
        pavSrc+=ctVerticesInRow*4;
        pavDst+=ctVerticesInRow*4;
      }
    }
  }

  // Lerp right border
  INDEX iRightNeighbour = tt.tt_aiNeighbours[NB_RIGHT];
  // if right neightbour exits
  if(iRightNeighbour>=0) {
    CTerrainTile &ttRight = _ptrTerrain->tr_attTiles[iRightNeighbour];
    const FLOAT fRightLerpFactor = ttRight.tt_fLodLerpFactor;
    // is right tile in highest lod and has smaller left factor
    if(ttRight.tt_iLod==0 && fRightLerpFactor<fLerpFactor) {
      TileLayer &ttl = ttRight.GetTileLayers()[iTileLayer];
      INDEX iFirstVertex = ctVerticesInRow*2-8;
      GFXVertex *pavSrc = &ttl.tl_avVertices[0];
      GFXVertex *pavDst = &_avLerpedTileLayerVertices[iFirstVertex];
      // for each quad
      for(INDEX ix=0;ix<ctQuadsPerRow;ix+=2) {
        GFXVertex *pavNRSrc = &pavSrc[ctVerticesInRow*2];
        GFXVertex *pavNRDst = &pavDst[ctVerticesInRow*2];

        Lerp(pavDst[7],pavSrc[2],pavSrc[0],pavNRSrc[2],fRightLerpFactor);
        pavNRDst[5] = pavDst[7];
        pavSrc+=ctVerticesInRow*4;
        pavDst+=ctVerticesInRow*4;
      }
    }
  }
}