/// <summary> /// /// </summary> /// <param name="page"></param> private void UnLoadPage(GeometryPage page) { //Calculate boundaries to unload PageInfo info = new PageInfo(); float halfPageSize = mMainGeom.PageSize * 0.5f; info.Bounds = new TBounds(0, 0, 0, 0); info.Bounds.Left = page.CenterPoint.x - halfPageSize; info.Bounds.Right = page.CenterPoint.x + halfPageSize; info.Bounds.Top = page.CenterPoint.z - halfPageSize; info.Bounds.Bottom = page.CenterPoint.z + halfPageSize; info.CenterPoint = page.CenterPoint; info.XIndex = page.mXIndex; info.ZIndex = page.mZIndex; info.UserData = page.mUserData; page.RemoveEntites(); mMainGeom.PageLoader.UnloadPage(info); page.mUserData = null; page.mNeedsUnload = false; page.ClearBoundingBox(); page.InactiveTime = 0; page.mIsLoaded = false; page.mIsFadeEnabled = false; }
/// <summary> /// /// </summary> /// <param name="point"></param> internal void ReloadGeometryPages(TBounds area) { //Determine which grid block contains the top-left corner int x1 = (int)System.Math.Floor(mGeomGridX * (area.Left - mGridBounds.Left) / mGridBounds.Width); int z1 = (int)System.Math.Floor(mGeomGridZ * (area.Top - mGridBounds.Top) / mGridBounds.Height); if (x1 < 0) { x1 = 0; } else if (x1 > mGeomGridX) { x1 = mGeomGridX; } if (z1 < 0) { z1 = 0; } else if (z1 > mGeomGridZ) { z1 = mGeomGridZ; } //...and the bottom right int x2 = (int)System.Math.Floor(mGeomGridX * (area.Right - mGridBounds.Left) / mGridBounds.Width); int z2 = (int)System.Math.Floor(mGeomGridZ * (area.Bottom - mGridBounds.Top) / mGridBounds.Height); if (x2 < 0) { x2 = 0; } else if (x2 > mGeomGridX) { x2 = mGeomGridX; } if (z2 < 0) { z2 = 0; } else if (z2 > mGeomGridZ) { z2 = mGeomGridZ; } for (int x = x1; x <= x2; ++x) { for (int z = z1; z <= z2; ++z) { GeometryPage page = GetGridPage(x, z); if (page.IsLoaded) { UnLoadPage(page); mLoadedList.Remove(page); } } } }
/// <summary> /// /// </summary> /// <param name="x"></param> /// <param name="z"></param> /// <param name="page"></param> private void SetGridPage(int x, int z, GeometryPage page) { if (x >= mGeomGridX || z >= mGeomGridZ) { throw new Exception("Grid dimension is out of bounds, GeometryPageManager.GetGridPage()"); } mGeomGrid[z * mGeomGridX + x] = page; }
/// <summary> /// /// </summary> internal void ReloadGeometry() { foreach (GeometryPage it in mLoadedList) { GeometryPage page = it; UnLoadPage(page); } mLoadedList.Clear(); }
/// <summary> /// /// </summary> /// <param name="point"></param> internal void ReloadGeometryPage(Vector3 point) { //Determine which grid block contains the given points int x = (int)System.Math.Floor(mGeomGridX * (point.x - mGridBounds.Left) / mGridBounds.Width); int z = (int)System.Math.Floor(mGeomGridZ * (point.z - mGridBounds.Top) / mGridBounds.Height); //Unload the grid block if it's in the grid area, and is loaded if (x >= 0 && z >= 0 && x < mGeomGridX && z < mGeomGridZ) { GeometryPage page = GetGridPage(x, z); if (page.IsLoaded) { UnLoadPage(page); mLoadedList.Remove(page); } } }
/// <summary> /// /// </summary> /// <param name="page"></param> private void LoadPage(GeometryPage page) { //Calculate page info PageInfo info = new PageInfo(); float halfPageSize = mMainGeom.PageSize * 0.5f; info.Bounds = new TBounds(0, 0, 0, 0); info.Bounds.Left = page.CenterPoint.x - halfPageSize; info.Bounds.Right = page.CenterPoint.x + halfPageSize; info.Bounds.Top = page.CenterPoint.z - halfPageSize; info.Bounds.Bottom = page.CenterPoint.z + halfPageSize; info.CenterPoint = page.CenterPoint; info.XIndex = page.mXIndex; info.ZIndex = page.mZIndex; info.UserData = page.mUserData; //Check if page needs unloading (if a delayed unload has been issued) if (page.mNeedsUnload) { page.RemoveEntites(); mMainGeom.PageLoader.UnloadPage(info); page.mUserData = null; page.mNeedsUnload = false; page.ClearBoundingBox(); } //Load the page page.SetRegion(info.Bounds.Left, info.Bounds.Top, info.Bounds.Right, info.Bounds.Bottom); mMainGeom.PageLoader.mGeomPage = page; mMainGeom.PageLoader.LoadPage(info); page.mUserData = info.UserData; page.Build(); page.IsVisible = page.mIsVisible; page.InactiveTime = 0; page.mIsLoaded = true; page.mIsFadeEnabled = false; }
/// <summary> /// /// </summary> /// <param name="shiftX"></param> /// <param name="shiftZ"></param> private void ScrollGridPages(int shiftX, int shiftZ) { //Check if the camera moved completely out of the grid if (shiftX > mGeomGridX || shiftX < -mGeomGridX || shiftZ > mGeomGridZ || shiftZ < -mGeomGridZ) { //If so, just reload all the tiles (reloading is accomplished by unloading - loading is automatic) for (int x = 0; x < mGeomGridX; x++) { for (int z = 0; z < mGeomGridZ; z++) { GeometryPage page = GetGridPage(x, z); if (page.IsLoaded) { UnLoadPage(page); mLoadedList.Remove(page); } float cx = shiftX * mMainGeom.PageSize; float cz = shiftZ * mMainGeom.PageSize; page.CenterPoint = new Vector3(page.CenterPoint.x + cx, page.CenterPoint.y, page.CenterPoint.z + cz); page.mXIndex += shiftX; page.mZIndex += shiftZ; } } } else//If not, scroll the grid by the X and Y values { if (shiftX > 0)//Scroll horizontally (X) { for (int z = 0; z < mGeomGridZ; z++) { //Temporarily store off-shifted pages first for (int x = 0; x < shiftX; x++) { GeometryPage page = GetGridPage(x, z); if (page.IsLoaded) { UnLoadPage(page); mLoadedList.Remove(page); } mScrollBuffer[x] = page; } //shift left int shiftedMidPoint = mGeomGridX - shiftX; for (int x = 0; x < shiftedMidPoint; x++) { SetGridPage(x, z, GetGridPage(x + shiftX, z)); } //Rotate temporary pages around to other side of grid for (int x = 0; x < shiftX; x++) { float cx = mGeomGridX * mMainGeom.PageSize; mScrollBuffer[x].CenterPoint = new Vector3( mScrollBuffer[x].CenterPoint.x + cx, mScrollBuffer[x].CenterPoint.y, mScrollBuffer[x].CenterPoint.z); mScrollBuffer[x].mXIndex += mGeomGridX; SetGridPage(x + shiftedMidPoint, z, mScrollBuffer[x]); } } } else if (shiftX < 0) { for (int z = 0; z < mGeomGridZ; ++z) { //Temporarily store off-shifted pages first int initialMidpoint = mGeomGridX + shiftX; for (int x = initialMidpoint; x < mGeomGridX; ++x) { GeometryPage page = GetGridPage(x, z); if (page.IsLoaded) { UnLoadPageDelayed(page); mLoadedList.Remove(page); } mScrollBuffer[x - initialMidpoint] = page; } //Shift right for (int x = mGeomGridX - 1; x >= -shiftX; x--) { SetGridPage(x, z, GetGridPage(x + shiftX, z)); } //Rotate temporary pages around to other side of grid for (int x = 0; x < -shiftX; ++x) { float cx = mGeomGridX * mMainGeom.PageSize; mScrollBuffer[x].CenterPoint = new Vector3( mScrollBuffer[x].CenterPoint.x - cx, mScrollBuffer[x].CenterPoint.y, mScrollBuffer[x].CenterPoint.z); mScrollBuffer[x].mXIndex -= mGeomGridX; SetGridPage(x, z, mScrollBuffer[x]); } } } //Scroll vertically (Z) if (shiftZ > 0) { for (int x = 0; x < mGeomGridX; x++) { //Temporarily store off-shifted pages first for (int z = 0; z < shiftZ; z++) { GeometryPage page = GetGridPage(x, z); if (page.IsLoaded) { UnLoadPageDelayed(page); mLoadedList.Remove(page); } mScrollBuffer[z] = page; } //Shift left int shiftedMidPoint = mGeomGridZ - shiftZ; for (int z = 0; z < shiftedMidPoint; z++) { SetGridPage(x, z, GetGridPage(x, z + shiftZ)); } //Rotate temporary pages around to other side of grid for (int z = 0; z < shiftZ; z++) { float cz = mGeomGridZ * mMainGeom.PageSize; mScrollBuffer[z].CenterPoint = new Vector3( mScrollBuffer[z].CenterPoint.x, mScrollBuffer[z].CenterPoint.y, mScrollBuffer[z].CenterPoint.z + cz); mScrollBuffer[z].mZIndex += mGeomGridZ; SetGridPage(x, z + shiftedMidPoint, mScrollBuffer[z]); } } } else if (shiftZ < 0) { for (int x = 0; x < mGeomGridX; x++) { //Temporarily store off-shifted pages first int initialMidpoint = mGeomGridZ + shiftZ; for (int z = initialMidpoint; z < mGeomGridZ; z++) { GeometryPage page = GetGridPage(x, z); if (page.IsLoaded) { UnLoadPageDelayed(page); mLoadedList.Remove(page); } mScrollBuffer[z - initialMidpoint] = page; } //shift right for (int z = mGeomGridZ - 1; z >= -shiftZ; z--) { SetGridPage(x, z, GetGridPage(x, z + shiftZ)); } //Rotate temporary pages around to other side of grid for (int z = 0; z < -shiftZ; z++) { float cz = mGeomGridZ * mMainGeom.PageSize; mScrollBuffer[z].CenterPoint = new Vector3( mScrollBuffer[z].CenterPoint.x, mScrollBuffer[z].CenterPoint.y, mScrollBuffer[z].CenterPoint.z - cz); mScrollBuffer[z].mZIndex -= mGeomGridZ; SetGridPage(x, z, mScrollBuffer[z]); } } } } }
/// <summary> /// /// </summary> /// <param name="page"></param> private void UnLoadPageDelayed(GeometryPage page) { page.mNeedsUnload = true; page.mIsLoaded = false; }
/// <summary> /// /// </summary> /// <param name="point"></param> /// <param name="radius"></param> internal void ReloadGeometryPages(Vector3 center, float radius) { //First calculate a square boundary to eliminate the search space TBounds area = new TBounds(center.x - radius, center.z - radius, center.x + radius, center.z + radius); //Determine which grid block contains the top-left corner int x1 = (int)System.Math.Floor(mGeomGridX * (area.Left - mGridBounds.Left) / mGridBounds.Width); int z1 = (int)System.Math.Floor(mGeomGridZ * (area.Top - mGridBounds.Top) / mGridBounds.Height); if (x1 < 0) { x1 = 0; } else if (x1 > mGeomGridX) { x1 = mGeomGridX; } if (z1 < 0) { z1 = 0; } else if (z1 > mGeomGridZ) { z1 = mGeomGridZ; } //...and the bottom right int x2 = (int)System.Math.Floor(mGeomGridX * (area.Right - mGridBounds.Left) / mGridBounds.Width); int z2 = (int)System.Math.Floor(mGeomGridZ * (area.Bottom - mGridBounds.Top) / mGridBounds.Height); if (x2 < 0) { x2 = 0; } else if (x2 > mGeomGridX) { x2 = mGeomGridX; } if (z2 < 0) { z2 = 0; } else if (z2 > mGeomGridZ) { z2 = mGeomGridZ; } //Scan all the grid blocks in the region float radiusSq = radius * radius; for (int x = x1; x <= x2; ++x) { for (int z = z1; z <= z2; ++z) { GeometryPage page = GetGridPage(x, z); if (page.IsLoaded) { Vector3 pos = page.CenterPoint; Real distX = (pos.x - center.x), distZ = (pos.z - center.z); Real distSq = distX * distX + distZ * distZ; if (distSq <= radius) { UnLoadPage(page); mLoadedList.Remove(page); } } } } }
/// <summary> /// /// </summary> /// <param name="deltaTime"></param> /// <param name="campos"></param> /// <param name="camSpeed"></param> /// <param name="enableCache"></param> /// <param name="prevManager"></param> internal void Update(long deltaTime, Vector3 campos, Vector3 camSpeed, bool enableCache, GeometryPageManager prevManager) { //-- Cache new geometry pages -- //Cache 1 page ahead of the view ranges float cacheDist = mFarTransDist + mMainGeom.PageSize; float cacheDistSq = cacheDist * cacheDist; //First calculate the general area where the pages will be processed // 0,0 is the left top corner of the bounding box int x1 = (int)System.Math.Floor( ((campos.x - cacheDist) - mGridBounds.Left) / mMainGeom.PageSize); int x2 = (int)System.Math.Floor( ((campos.x + cacheDist) - mGridBounds.Left) / mMainGeom.PageSize); int z1 = (int)System.Math.Floor( ((campos.z - cacheDist) - mGridBounds.Top) / mMainGeom.PageSize); int z2 = (int)System.Math.Floor( ((campos.z + cacheDist) - mGridBounds.Top) / mMainGeom.PageSize); if (mScrollBuffer != null) { //Check if the page grid needs to be scrolled int shiftX = 0, shiftZ = 0; if (x1 < 0) { shiftX = x1; } else if (x2 >= mGeomGridX - 1) { shiftX = x2 - (mGeomGridX - 1); } if (z1 < 0) { shiftZ = z1; } else if (z2 >= mGeomGridZ - 1) { shiftZ = z2 - (mGeomGridZ - 1); } if (shiftX != 0 || shiftZ != 0) { //Scroll grid ScrollGridPages(shiftX, shiftZ); //Update grid bounds and processing area mGridBounds.Left += shiftX * mMainGeom.PageSize; mGridBounds.Right += shiftX * mMainGeom.PageSize; mGridBounds.Top += shiftZ * mMainGeom.PageSize; mGridBounds.Bottom += shiftZ * mMainGeom.PageSize; x1 -= shiftX; x2 -= shiftX; z1 -= shiftZ; z2 -= shiftZ; } } else { // make sure that values are inbounds if (x2 >= mGeomGridX) { x2 = mGeomGridX - 1; } if (z2 >= mGeomGridZ) { z2 = mGeomGridZ - 1; } if (x1 < 0) { x1 = 0; } if (z1 < 0) { z1 = 0; } } //Now, in that general area, find what pages are within the cacheDist radius //Pages within the cacheDist radius will be added to the pending block list //to be loaded later, and pages within farDist will be loaded immediately. for (int x = x1; x <= x2; ++x) { for (int z = z1; z <= z2; ++z) { GeometryPage blk = GetGridPage(x, z); float dx = campos.x - blk.CenterPoint.x; float dz = campos.z - blk.CenterPoint.z; float distSq = dx * dx + dz * dz; //If the page is in the cache radius... if (distSq <= cacheDistSq) { //If the block hasn't been loaded yet, it should be if (!blk.IsLoaded) { //Test if the block's distance is between nearDist and farDist if (distSq >= mNearDistSq && distSq < mFarTransDistSq) { //If so, load the geometry immediately LoadPage(blk); mLoadedList.Add(blk); if (blk.IsPending) { mPendingList.Remove(blk); blk.IsPending = false; } } else { //Otherwise, add it to the pending geometry list (if not already) //Pages in then pending list will be loaded later (see below) if (!blk.IsPending) { mPendingList.Add(blk); blk.IsPending = true; } } } else { //Set the inactive time to 0 (since the page is active). This //must be done in order to keep it from expiring (and deleted). //This way, blocks not in the cache radius won't have their //inactivity clock reset, and they will expire in a few seconds. blk.InactiveTime = 0; } } } }//end for //Calculate cache speeds based on camera speed. This is important to keep the cache //process running smooth, because if the cache can't keep up with the camera, the //immediately visible pages will be forced to load instantly, which can cause //noticeable and sudden stuttering. The cache system results in smoother performance //because it smooths the loading tasks out across multiple frames. For example, //instead of loading 10+ blocks every 2 seconds, the cache would load 1 block every //200 milliseconds. float speed = MogreLibMath.Utility.Sqrt(camSpeed.x * camSpeed.x + camSpeed.z * camSpeed.z); long cacheInterval = 0; if (speed == 0) { cacheInterval = mMaxCacheInterval; } else { cacheInterval = (long)((mMainGeom.PageSize * 0.8f) / (speed * mPendingList.Count)); if (cacheInterval > mMaxCacheInterval) { cacheInterval = mMaxCacheInterval; } } int geomPageBegin = 0; int geomPageEnd = mPendingList.Count - 1; //GeometryPage i1 = null; GeometryPage i2 = null; //Now load a single geometry page periodically, based on the cacheInterval mCacheTimer += deltaTime; if (mCacheTimer >= cacheInterval && enableCache) { //Find a block to be loaded from the pending list //i1 = mPendingList[geomPageBegin]; // i2 = mPendingList[geomPageEnd]; //while (i1 != i2) GeometryPage[] pend = mPendingList.ToArray(); foreach (GeometryPage i1 in pend) { GeometryPage blk = i1; //Remove it from the pending list mPendingList.Remove(i1); //if (geomPageBegin < mPendingList.Count) // i1 = mPendingList[geomPageBegin++]; blk.IsPending = false; //If it's within the geometry cache radius, load it and break out of the loop float dx = campos.x - blk.CenterPoint.x; float dz = campos.z - blk.CenterPoint.z; float distSq = dx * dx + dz * dz; if (distSq <= cacheDistSq) { LoadPage(blk); mLoadedList.Add(blk); blk = mLoadedList[mLoadedList.Count - 1]; enableCache = false; break; } //Otherwise this will keep looping until an unloaded page is found } mCacheTimer = 0; } //-- Update existing geometry and impostors -- //Loop through each loaded geometry block int loadPageBegin = 0; int loadPageEnd = mLoadedList.Count - 1; //i1 = mLoadedList[loadPageBegin]; // i2 = mLoadedList[loadPageEnd]; float halfPageSize = mMainGeom.PageSize * 0.5f; //while (i1 != i2) GeometryPage[] load = mLoadedList.ToArray(); foreach (GeometryPage i1 in load) { GeometryPage blk = i1; //If the geometry has expired... if (blk.InactiveTime >= mInactivePageLife) { //Unload it UnLoadPage(blk); mLoadedList.Remove(i1); //if (loadPageBegin < mLoadedList.Count) // i1 = mLoadedList[loadPageBegin]; } else {//Update it's visibility/fade status based on it's distance from the camera bool visible = false; float dx = campos.x - blk.CenterPoint.x; float dz = campos.z - blk.CenterPoint.z; float distSq = dx * dx + dz * dz; float overlap = 0, tmp = 0; tmp = blk.BoundingBox.Maximum.x - halfPageSize; if (tmp > overlap) { overlap = tmp; } tmp = blk.BoundingBox.Maximum.z - halfPageSize; if (tmp > overlap) { overlap = tmp; } tmp = blk.BoundingBox.Minimum.x + halfPageSize; if (tmp > overlap) { overlap = tmp; } tmp = blk.BoundingBox.Minimum.z + halfPageSize; if (tmp > overlap) { overlap = tmp; } float pageLengthSq = MogreLibMath.Utility.Sqr((mMainGeom.PageSize + overlap) * 1.41421356f); if (distSq + pageLengthSq >= mNearDistSq && distSq - pageLengthSq < mFarTransDistSq) { //Fade the page when transitioning bool enable = false; float fadeNear = 0; float fadeFar = 0; if (mFadeEnabled && distSq + pageLengthSq >= mFarDistSq) { //Fade in visible = true; enable = true; fadeNear = mFarDist; fadeFar = mFarTransDist; } else if (prevManager != null && prevManager.mFadeEnabled && (distSq - pageLengthSq < prevManager.mFarTransDistSq)) { //Fade out visible = true; enable = true; fadeNear = prevManager.mFarDist + (prevManager.mFarTransDist - prevManager.mFarDist) * 0.5f;//This causes geometry to fade out faster than it fades in, avoiding a state where a transition appears semitransparent fadeFar = prevManager.mFarDist; } //apply fade settings if (enable != blk.IsFadeEnabled) { blk.SetFade(enable, fadeNear, fadeFar); blk.IsFadeEnabled = enable; } } //Non-fade visibility if (distSq > mNearDistSq && distSq < mFarDistSq) { visible = true; } //Update visibility if (visible) { //Show the page if it isn't visible if (!blk.IsVisible) { blk.IsVisible = true; } } else { //Hide the page if it's not already if (blk.IsVisible) { blk.IsVisible = false; } } //And update it blk.Update(); //if (loadPageBegin < mLoadedList.Count) // i1 = mLoadedList[loadPageBegin++]; } //Increment the inactivity timer for the geometry blk.InactiveTime += deltaTime; } }
/// <summary> /// /// </summary> /// <param name="bounds"></param> /// <returns></returns> internal void InitPages <T>(TBounds bounds) { // Calculate grid size, if left is Real minimum, it means that bounds are infinite // scrollBuffer is used as a flag. If it is allocated than infinite bounds are used // !!! Two cases are required because of the way scrolling is implemented // if it is redesigned it would allow to use the same functionality. if (bounds.Width < 0.00001f) { // In case of infinite bounds bounding rect needs to be calculated in a different manner, since // it represents local bounds, which are shifted along with the player's movements around the world. mGeomGridX = (int)((2 * mFarTransDist / mMainGeom.PageSize) + 4); mGridBounds.Top = 0; mGridBounds.Left = 0; mGridBounds.Right = mGeomGridX * mMainGeom.PageSize; mGridBounds.Bottom = mGeomGridX * mMainGeom.PageSize; // Allocate scroll buffer (used in scrolling the grid) mScrollBuffer = new GeometryPage[mGeomGridX]; //Note: All this padding and transition preparation is performed because even in infinite //mode, a local grid size must be chosen. Unfortunately, this also means that view ranges //and transition lengths cannot be exceeded dynamically with set functions. } else { //Bounded mode mGridBounds = bounds; // In case the devision does not give the round number use the next largest integer mGeomGridX = (int)System.Math.Ceiling(mGridBounds.Width / mMainGeom.PageSize); } mGeomGridZ = mGeomGridX;//Note: geomGridX == geomGridZ; Need to merge. //Allocate grid array mGeomGrid = new GeometryPage[mGeomGridX * mGeomGridZ]; for (int x = 0; x < mGeomGridX; ++x) { for (int z = 0; z < mGeomGridZ; ++z) { GeometryPage p = null; Type t = typeof(T); switch (t.Name) { case "GrassPage": p = new GrassPage(); break; case "BatchPage": p = new BatchPage(); break; case "ImpostorPage": p = new ImpostorPage(); break; default: throw new Exception("This GeometryPage is Unkown!,GeometryPageManager.Update!"); break; } p.Init(mMainGeom); float cx = 0, cy = 0, cz = 0; // 0,0 page is located at (gridBounds.left,gridBounds.top) corner of the bounds cx = ((x + 0.5f) * mMainGeom.PageSize) + mGridBounds.Left; cz = ((z + 0.5f) * mMainGeom.PageSize) + mGridBounds.Top; cy = 0.0f; p.CenterPoint = new Vector3(cx, cy, cz); p.mXIndex = x; p.mZIndex = z; p.mInactiveTime = 0; p.mIsLoaded = false; p.mNeedsUnload = false; p.mIsPending = false; p.mIsVisible = false; p.mUserData = null; p.ClearBoundingBox(); SetGridPage(x, z, p); } } }