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 }