/// <summary> /// /// </summary> public void Update() { //If no camera has been set, then return without doing anything if (mSceneCam == null) { return; } //Calculate time since last update long deltaTime = 0, tmp = 0; tmp = mTimer.Milliseconds; deltaTime = tmp - mLastTime; mLastTime = tmp; //Get camera position and speed Vector3 camPos = ConvertToLocal(mSceneCam.DerivedPosition); Vector3 camSpeed = Vector3.Zero; if (deltaTime == 0) { camSpeed = new Vector3(0, 0, 0); } else { camSpeed = (camPos - mOldCamPos) / deltaTime; } mOldCamPos = camPos; if (mPageLoader != null) { //Update the PageLoader mPageLoader.FrameUpdate(); //Update all the page managers bool enableCache = true; GeometryPageManager prevMgr = null; foreach (GeometryPageManager it in mMangerList) { GeometryPageManager mgr = it; mgr.Update(deltaTime, camPos, camSpeed, enableCache, prevMgr); prevMgr = mgr; } } //Update misc. subsystems StaticBillboardSet.UpdateAll(ConvertToLocal(Camera.DerivedDirection)); }
/// <summary> /// /// </summary> /// <param name="mgr"></param> /// <param name="maxRange"></param> /// <param name="transitionLength"></param> protected void AddDetailLevel(GeometryPageManager mgr, float maxRange, float transitionLength) { //Calculate the near range float minRange = 0; if (mMangerList.Count > 0) { GeometryPageManager lastMgr = mMangerList[mMangerList.Count - 1]; minRange = lastMgr.FarRange; } //Error check if (maxRange <= minRange) { throw new Exception("Closer detail levels must be added before farther ones"); } //Setup the new manager mgr.NearRange = minRange; mgr.FarRange = maxRange; mgr.Transition = transitionLength; mMangerList.Add(mgr); }
/// <summary> /// /// </summary> /// <param name="maxRange"></param> /// <param name="transitionLength"></param> /// <returns></returns> public GeometryPageManager AddDetailLevel <T>(float maxRange, float transitionLength) where T : GeometryPage { //Create a new page manager GeometryPageManager mgr = new GeometryPageManager(this); //If vertex shaders aren't supported, don't use transitions Root root = Root.Singleton; float transLength = transitionLength; if (!root.RenderSystem.Capabilities.HasCapability(MogreLibGraphics.Capabilities.VertexPrograms)) { transLength = 0; } //Add it to the list (also initializing maximum viewing distance) AddDetailLevel(mgr, maxRange, transLength); //And initialize the paged (dependent on maximum viewing distance) mgr.InitPages <T>(Bounds); return(mgr); }
/// <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; } }