Example #1
0
float IScrollMathModel::PageX() const
{
	return OffsetX() / mCurrentWindow.Width();
}
  // draw a ground-hugging sprite
  //
  Bucket * RenderGroundSprite( const Vector &origin, F32 radx, F32 rady, U32 clipFlags, F32 viewz, const Bitmap *texture, Color color, U32 blend, UVPair uv0, UVPair uv1, UVPair uv2, const GETHEIGHTPROCPTR getHeightProc, U16 sorting) // UVPair(0.0f,1.0f), UVPair(1.0f,1.0f), UVPair(1.0f,0.0f), GetHeight
  {
  #ifdef DOSTATISTICS
    Statistics::tempTris = 0;
  #endif

    clipFlags;
    viewz;

    if (texture)
    {
      uv0.u += texture->UVShiftWidth();
      uv0.v += texture->UVShiftHeight();
      uv1.u += texture->UVShiftWidth();
      uv1.v += texture->UVShiftHeight();
      uv2.u += texture->UVShiftWidth();
      uv2.v += texture->UVShiftHeight();
    }

 	  // set the primitive description
	  Vid::SetBucketPrimitiveDesc(PT_TRIANGLELIST, FVF_TLVERTEX,
		  DP_DONOTUPDATEEXTENTS | DP_DONOTLIGHT | DP_DONOTCLIP | RS_TEXCLAMP | blend);

	  // set the world transform matrix
	  Vid::SetWorldTransform(Matrix::I);

    // set material, texture, and force translucency
    Vid::SetBucketMaterial( Vid::defMaterial);
	  Vid::SetBucketTexture( texture, TRUE, 0, RS_TEXCLAMP | blend);
//	  Vid::SetTranBucketZ(viewz);
    Vid::SetTranBucketZMax( sorting);

	  // sprite corners in world coordinates
	  F32 meterX0 = origin.x - radx + OffsetX();
	  F32 meterZ0 = origin.z - rady + OffsetZ();
    F32 meterX1 = origin.x + radx + OffsetX();
	  F32 meterZ1 = origin.z + rady + OffsetZ();

	  // get corners of the grid covered by the sprite
    // get grid larger than sprite; adjust texture coords
    // round down to grid point
    // round up to grid point

    U16 cw = Utils::FP::SetRoundDownMode();
	  S32 cellX0 = Utils::FastFtoL( meterX0 * CellPerMeter());
	  S32 cellZ0 = Utils::FastFtoL( meterZ0 * CellPerMeter());

    Utils::FP::SetRoundUpMode();
	  S32 cellX1 = Utils::FastFtoL( meterX1 * CellPerMeter());
	  S32 cellZ1 = Utils::FastFtoL( meterZ1 * CellPerMeter());

    Utils::FP::RestoreMode( cw);

	  // dimensions of sprite in grid squares
	  S32 sizex = (cellX1 - cellX0 + 1);
	  S32 sizez = (cellZ1 - cellZ0 + 1);
    if (U32(sizex * sizez + 1) >= Vid::renderState.maxVerts || U32(sizex * sizez * 6) >= Vid::renderState.maxIndices)     // FIXME
    {
      LOG_WARN( ("Shadow too big") );
      return NULL;
    }
#if 0
    // verify buffer size
    ASSERT();
#endif

	  // lock primitive memory
    VertexTL * vertmem;
    U16 * indexmem;
    U32 heapSize = Vid::Heap::ReqVertex( &vertmem, &indexmem);

    VertexTL * pvert = vertmem;
    U16 * pindex = indexmem;

    // delta meters per grid
	  F32 dm = (F32) MeterPerCell();

	  F32 dx12 = meterX0 - meterX0;
	  F32 dx02 = meterX0 - meterX1;
    F32 dz12 = meterZ0 - meterZ1;
	  F32 dz02 = meterZ0 - meterZ1;
	  F32 dx = dx12 * dz02 - dx02 * dz12;
	  if (dx == 0.0f)
    {
      dx = F32_EPSILON;
    }
	  dx = 1.0f / dx;
	  F32 dz = -dx;

    // delta texture coords
	  F32 du12 = (F32) (uv1.u - uv2.u);
	  F32 du02 = (F32) (uv0.u - uv2.u);
	  F32 dudx = (du12 * dz02 - du02 * dz12) * dx;
	  F32 dudz = (du12 * dx02 - du02 * dx12) * dz;

	  F32 dv12 = (F32) (uv1.v - uv2.v);
	  F32 dv02 = (F32) (uv0.v - uv2.v);
	  F32 dvdx = (dv12 * dz02 - dv02 * dz12) * dx;
	  F32 dvdz = (dv12 * dx02 - dv02 * dx12) * dz;

	  // texture coordinates of southwest grid point
    dx = (cellX0 * dm - meterX0);
    dz = (cellZ0 * dm - meterZ0);
    F32 u = uv0.u + dx * dudx + dz * dudz;
	  F32 v = uv0.v + dx * dvdx + dz * dvdz;

	  // texture coordinate delta per grid
	  dudx *= dm;
	  dvdx *= dm;
	  dudz *= dm;
	  dvdz *= dm;

	  // sprite grid corners in world coordinates
	  meterX0 = cellX0 * dm - OffsetX();
	  meterZ0 = cellZ0 * dm - OffsetZ();
    meterX1 = cellX1 * dm - OffsetX();
	  meterZ1 = cellZ1 * dm - OffsetZ();

    // generate vertices on grid cell points
    S32 cellX, cellZ;
	  for (cellZ = cellZ0; cellZ <= cellZ1; cellZ++, meterZ0 += dm, u += dudz, v += dvdz)
	  {
      F32 xx = meterX0;
      F32 uu = u;
      F32 vv = v;

		  for (cellX = cellX0; cellX <= cellX1; cellX++, xx += dm, uu += dudx, vv += dvdx)
		  {
        // allow cells at Width and Height
        //
        if (cellX < 0 || cellX > (S32)CellWidth() || cellZ < 0 || cellZ > (S32)CellHeight())
        {
          pvert++;
          continue;
        }
			  pvert->vv.x     = xx;
        pvert->vv.y     = (*getHeightProc)( cellX, cellZ);
			  pvert->vv.z     = meterZ0;
			  pvert->diffuse  = color;
			  pvert->specular = RGBA_MAKE(0x00, 0x00, 0x00, 0xFF);
			  pvert->u        = uu;
			  pvert->v        = vv;
			  pvert++;
		  }
    }

	  // generate indices
	  U32 offset = 0;

    U32 sizex1 = sizex + 1;

	  for (cellZ = cellZ0; cellZ < cellZ1; cellZ++)
	  {
		  for (cellX = cellX0; cellX < cellX1; cellX++)
		  {
        if (cellX < 0 || cellX >= (S32)CellWidth() || cellZ < 0 || cellZ >= (S32)CellHeight())
        {
          // cell off map
          offset++;
          continue;
        }
			  // upper left triangle
			  *pindex++ = (U16) offset;
			  *pindex++ = (U16)(offset + sizex);
			  *pindex++ = (U16)(offset + sizex1);

			  // lower right triangle
			  *pindex++ = (U16) offset;
			  *pindex++ = (U16)(offset + sizex1);
			  *pindex++ = (U16)(offset + 1);

			  // advance the base offset
			  offset++;
      }
		  // advance the base offset
		  offset++;
    }

    Bucket * bucket = NULL;
    // submit for projection and clipping
  #if 0
    if (clipFlags == clipNONE)
    {
      bucket = Vid::CurCamera().ProjectNoClipBias( NULL, vertmem, pvert - vertmem, indexmem, pindex - indexmem);
    }
    else
  #endif
    {
      bucket = Vid::ProjectClip( vertmem, pvert - vertmem, indexmem, pindex - indexmem);
    }

    Vid::Heap::Restore( heapSize);

  #ifdef DOSTATISTICS
    Statistics::groundSpriteTris = Statistics::groundSpriteTris + Statistics::tempTris;
  #endif

    return bucket;
  }
  // draw the terrain
  //
  void RenderV()
  {
    if (Vid::renderState.status.mirror)
    {
      waterInView = FALSE;
      waterRect.p0.x = waterRect.p0.y =  S32_MAX;
      waterRect.p1.x = waterRect.p1.y = -S32_MAX;
//      lowWaterHeight = F32_MAX;
    }

    if (lowWaterFirst)
    {
      lowWaterHeight = 0;
      lowWaterCount = 0;
    }

    Vid::SetBucketPrimitiveDesc(
      PT_TRIANGLELIST,
      FVF_CVERTEX,
      RS_BLEND_DEF | renderFlags);

    Vid::SetWorldTransform( Matrix::I);
    Vid::SetBucketMaterial( Vid::defMaterial);
    Vid::SetTranBucketZMax( Vid::sortTERRAIN0 + 1);

    // returns rect of meters that might be visible
	  Area<S32> rect;
    GetVisibleClusterRect( rect);

    S32 l = rect.p0.x;
    S32 t = rect.p0.y;
    if (l < 0)
    {
      l = 0;
    }
    if (t < 0)
    {
      t = 0;
    }

  #ifdef DOSTATISTICS
    Statistics::tempTris = 0;
  #endif

    // clip to the actual terrain rectangle
/*
    if (rect.p0.x < 0)
    {
      rect.p0.x = 0;
    }
    if (rect.p1.x > (S32) clusWidth)
    {
      rect.p1.x = clusWidth;
    }
    if (rect.p0.y < 0)
    {
      rect.p0.y = 0;
    }
    if (rect.p1.y > (S32) clusHeight)
    {
      rect.p1.y = clusHeight;
    }
*/
    // re-convert to meters
    rect.p0.y *= meterPerClus;
    rect.p1.y *= meterPerClus;
    rect.p0.x *= meterPerClus;
    rect.p1.x *= meterPerClus;

    S32 z, x;
    for (z = rect.p0.y; z < rect.p1.y; z += meterPerClus)
    {
      for (x = rect.p0.x; x < rect.p1.x; x += meterPerClus)
      {
        F32 coz = (F32) z * clusPerMeter;
        F32 cox = (F32) x * clusPerMeter;

        S32 clusOffz = Utils::FtoL( coz);
        S32 clusOffx = Utils::FtoL( cox);

        if (clusOffz < 0 || clusOffz >= (S32) clusHeight || clusOffx < 0 || clusOffx >= (S32) clusWidth)
        {
          if (!Vid::renderState.status.mirrorIn)
          {
            RenderClusterOffMapV( x, z);
          }
          continue;
        }

        Cluster & clus = clusList[clusOffz * clusWidth + clusOffx];
        Bounds  & bounds = clus.bounds;

        Vector viewOrigin;
        U32 clipFlags = Vid::CurCamera().BoundsTestOrigin( bounds.Offset(), bounds, &viewOrigin);
        if (clipFlags == clipOUTSIDE)
        {
          // cluster is completely outside the view frustrum
          continue;
        }
        clus.zDepth = viewOrigin.z;
        U32 offset = clusOffz * cellPerClus * heightField.cellPitch + clusOffx * cellPerClus;
        S32 xs = x - Utils::FtoL(OffsetX());    // FIXME
        S32 zs = z - Utils::FtoL(OffsetZ());

        if (clus.status.water)
        {
          if (Vid::renderState.status.mirror)
          {
            waterInView = TRUE;
            if (clusOffx < waterRect.p0.x)
            {
              waterRect.p0.x = clusOffx;
            }
            if (clusOffz < waterRect.p0.y)
            {
              waterRect.p0.y = clusOffz;
            }
            if (clusOffx > waterRect.p1.x)
            {
              waterRect.p1.x = clusOffx;
            }
            if (clusOffz > waterRect.p1.y)
            {
              waterRect.p1.y = clusOffz;
            }
          }
        }

        Vid::Light::SetActiveList( bounds.Offset(), bounds);

        RenderClusterV( clus, xs, zs, offset, 1, 1, clipFlags);
      }
    }

  #ifdef DOSTATISTICS
    Statistics::terrainTris = Statistics::tempTris;
  #endif
  }
  // draw just the water
  //
  void RenderMirrorMaskV( WaterRegion * waterR)
  {
    Bool alpha = Vid::SetAlphaState( TRUE);

    Vid::SetWorldTransform( Matrix::I);
    Vid::SetMaterial( mirrorMaterial);
    Vid::SetTexture( waterTex, 0, RS_BLEND_MODULATE);

    lowWaterHeight = 0;
    lowWaterCount = 0;
    lowWaterFirst = FALSE;

    // returns rect of meters that might be visible
	  Area<S32> rect;
    GetVisibleClusterRect( rect);

  #ifdef DOSTATISTICS
    Statistics::tempTris = 0;
  #endif

    // clip to the actual terrain rectangle
    if (rect.p0.x < 0)
    {
      rect.p0.x = 0;
    }
    if (rect.p1.x > (S32) clusWidth)
    {
      rect.p1.x = clusWidth;
    }
    if (rect.p0.y < 0)
    {
      rect.p0.y = 0;
    }
    if (rect.p1.y > (S32) clusHeight)
    {
      rect.p1.y = clusHeight;
    }

    // re-convert to meters
    rect.p0.y *= meterPerClus;
    rect.p1.y *= meterPerClus;
    rect.p0.x *= meterPerClus;
    rect.p1.x *= meterPerClus;

    S32 z, x;
    for (z = rect.p0.y; z < rect.p1.y; z += meterPerClus)
    {
      for (x = rect.p0.x; x < rect.p1.x; x += meterPerClus)
      {
        F32 coz = (F32) z * clusPerMeter;
        F32 cox = (F32) x * clusPerMeter;

        S32 clusOffz = Utils::FtoL( coz);
        S32 clusOffx = Utils::FtoL( cox);

        Cluster & clus = clusList[clusOffz * clusWidth + clusOffx];

	      if (!clus.status.water || clus.waterIndex != waterR - waterList.data)
        {
          continue;
        }

        Vector viewOrigin;
        U32 clipFlags = Vid::CurCamera().BoundsTestOrigin( clus.bounds.Offset(), clus.bounds, &viewOrigin);
        if (clipFlags == clipOUTSIDE)
        {
          // cluster is completely outside the view frustrum
          continue;
        }

        Vid::Light::SetActiveList( clus.bounds.Offset(), clus.bounds);

//        U32 offset = clusOffz * cellPerClus * heightField.cellPitch + clusOffx * cellPerClus;
        S32 xs = x - Utils::FtoL(OffsetX());    // FIXME
        S32 zs = z - Utils::FtoL(OffsetZ());

        U32 offset = clusOffz * cellPerClus * heightField.cellPitch + clusOffx * cellPerClus;

        RenderCellMirrorMaskV( clus, xs, zs, offset, clipFlags);

        if (!Vid::Var::Terrain::shroud || clus.shroudCount != 25)
        {
          lowWaterHeight += clus.waterHeight;
          lowWaterCount++;
        }
      }
    }

  #ifdef DOSTATISTICS
    Statistics::terrainTris = Statistics::tempTris;
  #endif

    Vid::SetAlphaState( alpha);
  }
  void RenderClusterOffMapV( S32 x, S32 z)
  {
    S32 xc = x / (S32) heightField.meterPerCell;
    S32 zc = z / (S32) heightField.meterPerCell;
    ASSERT(xc != 0 || zc != 0);
/*
    if (zc > 0 || xc < heightField.cellWidth)
    {
      return;
    }
*/
    F32 xs = F32(x) - OffsetX();
    F32 zs = F32(z) - OffsetZ();

    F32 xend = xs + (F32) meterPerClus;
    F32 zend = zs + (F32) meterPerClus;

    F32 xy[5], zy[5], dx, dxx, dz = 0, dzz = 0, ymin = F32_MAX, ymax = -F32_MAX;

    S32 zcc = zc;
    S32 xcc = xc;

    S32 corners = 0;

    if (xc < 0)
    {
      for (U32 i = 0; i < 5; i++, zcc++)
      {
        S32 tzc = zcc < 0 ? 0 : zcc > (S32) heightField.cellHeight ? heightField.cellHeight : zcc;

        xy[i] = heightField.cellList[ tzc * heightField.cellPitch].height;

        ymin = Min<F32>( ymin, xy[i]);
        ymax = Max<F32>( ymax, xy[i]);
      }
      dx = (F32) -xc * (F32) heightField.meterPerCell;
      dxx =  - (F32) heightField.meterPerCell;

      corners |= 1;
    }
    else if (xc >= (S32) heightField.cellWidth)
    {
      for (U32 i = 0; i < 5; i++, zcc++)
      {
        S32 tzc = zcc < 0 ? 0 : zcc > (S32) heightField.cellHeight ? heightField.cellHeight : zcc;

        xy[i] = heightField.cellList[ tzc * heightField.cellPitch + heightField.cellWidth].height;

        ymin = Min<F32>( ymin, xy[i]);
        ymax = Max<F32>( ymax, xy[i]);
      }
      dx = (F32(xc) - F32(heightField.cellWidth)) * (F32) heightField.meterPerCell;
      dxx = (F32) heightField.meterPerCell;

      corners |= 1;
    }
    else
    {
      for (U32 i = 0; i < 5; i++, zcc++)
      {
        S32 tzc = zcc < 0 ? 0 : zcc > (S32) heightField.cellHeight ? heightField.cellHeight : zcc;

        xy[i] = heightField.cellList[ tzc * heightField.cellPitch + xc].height;

        ymin = Min<F32>( ymin, xy[i]);
        ymax = Max<F32>( ymax, xy[i]);
      }
      dx = 0;
      dxx = (F32) heightField.meterPerCell;
    }

    if (zc < 0)
    {
      for (U32 i = 0; i < 5; i++, xcc++)
      {
        S32 txc = xcc < 0 ? 0 : xcc > (S32) heightField.cellWidth  ? heightField.cellWidth : xcc;

        zy[i] = heightField.cellList[ txc].height;

        ymin = Min<F32>( ymin, zy[i]);
        ymax = Max<F32>( ymax, zy[i]);
      }
      dz = (F32) -zc * (F32) heightField.meterPerCell;
      dzz = - (F32) heightField.meterPerCell;

      corners |= 2;
    }
    else if (zc >= (S32) heightField.cellHeight)
    {
      for (U32 i = 0; i < 5; i++, xcc++)
      {
        S32 txc = xcc < 0 ? 0 : xcc > (S32) heightField.cellWidth  ? heightField.cellWidth : xcc;

        zy[i] = heightField.cellList[ heightField.cellHeight * heightField.cellPitch + txc].height;
        
        ymin = Min<F32>( ymin, zy[i]);
        ymax = Max<F32>( ymax, zy[i]);
      }

      dz = F32(zc - heightField.cellHeight) * (F32) heightField.meterPerCell;
      dzz = (F32) heightField.meterPerCell;

      corners |= 2;
    }

//  DyDx = (dy12 * dz02 - dy02 * dz12) *  dx;
//	DyDz = (dy12 * dx02 - dy02 * dx12) * -dx;

    Vector offset( (xend + xs) * .5f, (ymin + ymax) * .5f, (zend + zs) * .5f); 
    Bounds bounds;
    bounds.Set( (F32) fabs( xend - xs), ymax - ymin, (F32) fabs( zend - zs)); 

    U32 clipFlags = Vid::CurCamera().BoundsTestBox( offset, bounds);
    if (clipFlags == clipOUTSIDE)
    {
      // cluster is completely outside the view frustrum
      return;
    }

    Vid::SetBucketFlags( RS_BLEND_DEF | DP_DONOTLIGHT | renderFlags | ((clipFlags & clipALL) ? 0 : DP_DONOTCLIP) );

    Vid::SetTranBucketZMax( Vid::sortTERRAIN0 + 1);
    Vid::SetBucketMaterial( Vid::defMaterial);
    Vid::SetBucketTexture( NULL, FALSE);

    F32 dxy[5], dzy[5];
	U32 iz = 0;
    for (; iz < 5; iz++)
    {
      dxy[iz] = (offMapHeight - xy[iz]) / 1000;
      dzy[iz] = (offMapHeight - zy[iz]) / 1000;
    }

    VertexC * vertmem;
    U16 *    indexmem;
    if (!Vid::LockIndexedPrimitiveMem( (void **)&vertmem, 25, &indexmem, 96))
    {
      LOG_WARN( ("Terrain::RenderCluster: can't lock bucket!") );
      return;
    }
    VertexC * v = vertmem;

    iz = 0;
    for ( zs; zs <= zend; zs += heightField.meterPerCell, iz++, dz += dzz, zc++)
    {
      U32 ix = 0;
      F32 ddx = dx;
      S32 xcc = xc;
      for (F32 x = xs; x <= xend; x += heightField.meterPerCell, ix++, v++, ddx += dxx, xcc++)
      {
        v->vv.x = x;
        v->vv.z = zs;
        v->nv.Zero();
        v->diffuse  = 0xff000000;

        switch (corners)
        {
        case 3:
        {
          v->vv.y = xy[iz] + dxy[iz] * ddx;

          F32 zz = zs < 0 ? -zs : zs >= (S32) heightField.meterHeight ? zs - heightField.meterHeight : zs;
          F32 xx = x  < 0 ? -x  : x  >= (S32) heightField.meterWidth  ? x  - heightField.meterWidth  : x;

          if (zz > xx)
          {
            F32 dy = zy[ix] + dzy[ix] * dz;
            dy = (dy - v->vv.y) / zz;

            v->vv.y += dy * (zz - xx);
          }
          break;
        }
        case 2:
          v->vv.y = zy[ix] + dzy[ix] * dz;
          break;
        case 1:
          v->vv.y = xy[iz] + dxy[iz] * ddx;
          break;
        }
      }
    }
    Utils::Memcpy( indexmem, clusterI, 96 << 1);

    Vid::UnlockIndexedPrimitiveMem( 25, 96);

#ifdef DOSTATISTICS
    if (clipFlags == clipNONE)
    {
      Statistics::noClipTris += 2;
    }
    else
    {
      Statistics::clipTris += 2;
    }
#endif

  }