//--------------------------------------------------------------------- void TerrainQuadTreeNode::mergeIntoBounds(long x, long y, const VEC3& pos) { if (pointIntersectsNode(x, y)) { VEC3 localPos = pos - mLocalCentre; mAABB.Merge(localPos); mBoundingRadius = std::max(mBoundingRadius, localPos.GetLength()); if (!isLeaf()) { for (int i = 0; i < 4; ++i) mChildren[i]->mergeIntoBounds(x, y, pos); } } }
//--------------------------------------------------------------------- bool TerrainQuadTreeNode::calculateCurrentLod(float cFactor) { mSelfOrChildRendered = false; // early-out /* disable this, could cause 'jumps' in LOD as children go out of frustum if (!cam->isVisible(mMovable->getWorldBoundingBox(true))) { mCurrentLod = -1; return mSelfOrChildRendered; } */ // Check children first int childRenderedCount = 0; if (!isLeaf()) { for (int i = 0; i < 4; ++i) { if (mChildren[i]->calculateCurrentLod(cFactor)) ++childRenderedCount; } } if (childRenderedCount == 0) { // no children were within their LOD ranges, so we should consider our own VEC3 localPos = g_env.pSceneMgr->GetCamera()->GetPos() - mLocalCentre - mTerrain->getPosition(); float dist; if (g_env.pSceneMgr->GetTerrainOptions()->getUseRayBoxDistanceCalculation()) { // Get distance to this terrain node (to closest point of the box) // head towards centre of the box (note, box may not cover mLocalCentre because of height) //VEC3 dir(mAABB.getCenter() - localPos); //dir.normalise(); //Ray ray(localPos, dir); //std::pair<bool, float> intersectRes = Math::intersects(ray, mAABB); //// ray will always intersect, we just want the distance //dist = intersectRes.second; _AST(0); } else { // distance to tile centre dist = localPos.GetLength(); // deduct half the radius of the box, assume that on average the // worst case is best approximated by this dist -= (mBoundingRadius * 0.5f); } // For each LOD, the distance at which the LOD will transition *downwards* // is given by // distTransition = maxDelta * cFactor; uint32 lodLvl = 0; mCurrentLod = -1; for (LodLevelList::iterator i = mLodLevels.begin(); i != mLodLevels.end(); ++i, ++lodLvl) { // If we have no parent, and this is the lowest LOD, we always render // this is the 'last resort' so to speak, we always enoucnter this last if (lodLvl+1 == mLodLevels.size() && !mParent) { mCurrentLod = lodLvl; mSelfOrChildRendered = true; mLodTransition = 0; } else { // check the distance LodLevel* ll = *i; // Calculate or reuse transition distance float distTransition; if (Common::Equal(cFactor, ll->lastCFactor)) distTransition = ll->lastTransitionDist; else { distTransition = ll->maxHeightDelta * cFactor; ll->lastCFactor = cFactor; ll->lastTransitionDist = distTransition; } if (dist < distTransition) { // we're within range of this LOD mCurrentLod = lodLvl; mSelfOrChildRendered = true; if (mTerrain->_getMorphRequired()) { // calculate the transition percentage // we need a percentage of the total distance for just this LOD, // which means taking off the distance for the next higher LOD // which is either the previous entry in the LOD list, // or the largest of any children. In both cases these will // have been calculated before this point, since we process // children first. Distances at lower LODs are guaranteed // to be larger than those at higher LODs float distTotal = distTransition; if (isLeaf()) { // Any higher LODs? if (i != mLodLevels.begin()) { LodLevelList::iterator prev = i - 1; distTotal -= (*prev)->lastTransitionDist; } } else { // Take the distance of the lowest LOD of child const LodLevel* childLod = mChildWithMaxHeightDelta->getLodLevel( mChildWithMaxHeightDelta->getLodCount()-1); distTotal -= childLod->lastTransitionDist; } // fade from 0 to 1 in the last 25% of the distance float distMorphRegion = distTotal * 0.25f; float distRemain = distTransition - dist; mLodTransition = 1.0f - (distRemain / distMorphRegion); mLodTransition = std::min(1.0f, mLodTransition); mLodTransition = std::max(0.0f, mLodTransition); // Pass both the transition % and target LOD (GLOBAL current + 1) // this selectively applies the morph just to the // vertices which would drop out at this LOD, even // while using the single shared vertex data mTerrain->m_cbTerrain.lodMorph = VEC2(mLodTransition, mCurrentLod + mBaseLod + 1); } // since LODs are ordered from highest to lowest detail, // we can stop looking now break; } } } } else { // we should not render ourself mCurrentLod = -1; mSelfOrChildRendered = true; if (childRenderedCount < 4) { // only *some* children decided to render on their own, but either // none or all need to render, so set the others manually to their lowest for (int i = 0; i < 4; ++i) { TerrainQuadTreeNode* child = mChildren[i]; if (!child->isSelfOrChildRenderedAtCurrentLod()) { child->setCurrentLod(child->getLodCount()-1); child->setLodTransition(1.0); } } } // (childRenderedCount < 4) } // (childRenderedCount == 0) return mSelfOrChildRendered; }