#include "mountains.h" #include #include "geoworld.h" namespace geoworld { template< typename BFn > class SeedModifiedMidpointDisplacement : public BFn { private: const float _fSeedObedience, _fComplSeedObedience, _fMntRangeWidth; const TaperFn _taper; struct NearestSeedResult { mars::Magnitudinal< float > distance; float elevation; }; class ExpFn { private: float _fMntDistGaussXMult; public: ExpFn (const float fMntDistGaussXMult) : _fMntDistGaussXMult(fMntDistGaussXMult) {} inline const float f (const float x) const { return expf(-x / _fMntDistGaussXMult); } }; class CachedExpFn : public mars::CacheFnProxy< float, ExpFn > { public: CachedExpFn (const float fMntDistGaussXMult) : mars::CacheFnProxy< float, ExpFn >(ExpFn(fMntDistGaussXMult), 0, mars::SQ(static_cast< float > (LockedGWSection::MAX_LOCK_DIM)), LockedGWSection::MAX_LOCK_DIM) {} } _fnchExp; MountainGM::SeedList::const_iterator _iSeeds0, _iSeedsN; float _fSeedCount; const LockedGWSection::View * _pView; int _nHalfStride, _c; short compute (const int x, const int y, const short v) const { float fMnt = 0.0f; float fDistSQ = std::numeric_limits< float >::max(); for (MountainGM::SeedList::const_iterator i = _iSeeds0; i != _iSeedsN; ++i) { const float f = mars::SQ(i->x - x) + mars::SQ(i->y - y); if (f < fDistSQ) fDistSQ = f; // TODO: V-Tune attributes literally half of the performance issues to this line fMnt += i->z * _fnchExp.f(f); } fMnt /= _fSeedCount; return static_cast ( _taper.blend( sqrt(fDistSQ), static_cast (_pView->getHeightMap() ->getw(x, y)), static_cast (v) * _fComplSeedObedience + fMnt * _fSeedObedience ) ); } public: inline SeedModifiedMidpointDisplacement (const float fSeedObedience, const short nMntRangeWidth, const float fCoarseness, const float fFalloff) : BFn (nMntRangeWidth, fCoarseness), // HACK: Use the mountain range width since PinkFn only uses this to generate a random height-delta _fSeedObedience(fSeedObedience), _fMntRangeWidth(static_cast< float > (nMntRangeWidth)), _fComplSeedObedience(1.0f - fSeedObedience), _fnchExp(2 * mars::SQ(static_cast (nMntRangeWidth))), _taper(TaperFn::createTaperFn(fFalloff * nMntRangeWidth)) {} inline void init (const LockedGWSection::View * pView, const MountainGM::SeedList & seeds) { _pView = pView; _iSeeds0 = seeds.begin(); _iSeedsN = seeds.end(); _fSeedCount = static_cast< float > (seeds.size()); } inline void iteration (const int nStride, const int c) { BFn::iteration(nStride, c); _c = c; _nHalfStride = nStride / 2; } inline float getMaxMountainRadius () const { return _taper.outside; } inline short operator () (int & state, const int x, const int y, const short & a, const short & b, const short & c) { return compute(x, y, BFn::operator () (state, x, y, a, b, c)); } inline short operator () (int & state, const int x, const int y, const short & a, const short & b, const short & c, const short & d) { return compute(x, y, BFn::operator () (state, x, y, a, b, c, d)); } }; void MountainGM::init(SynthesisSession & manager, const IGeoWorldAccessor & accessor, const unsigned short nMaxWidth, const unsigned short nMaxHeight) { using namespace mars; vector3Df vdir = mars::U( mars::vector3Df(mars::RANDf(), mars::RANDf(), 0.0f) ), vrange; BBox< float > bbox0(0,0,0,0), bbox(0,0,0,0); const float fWidth = static_cast< float > (_nMntRangeWidth), fDisplace = fWidth * _fClumpMaxDisplace, fAvgHeight = static_cast< float > (_nMntRangeAvgHeight), fHeightFlux = fAvgHeight * _fPeakHeightFluxPct, fPeakStep = fWidth * _fCoarseness; _seeds.clear(); const float fMaxMtnRadius = SeedModifiedMidpointDisplacement< MidpointDisplacementGrid< GeoHeightMap::Precision >::BrownianFn > (1.0f, _nMntRangeWidth, _fCoarseness, _fFalloff).getMaxMountainRadius(); for (unsigned int c = 0; c < _nNumMountains; ++c) { const unsigned short nClumpCount = _mmnClumpSize.next(); for (unsigned int c = 0; c < nClumpCount; ++c) { vector3Df v = vrange + vector3Df ( RANDf(fDisplace * 2) - fDisplace, RANDf(fDisplace * 2) - fDisplace, fAvgHeight - RANDf(fHeightFlux) ); bbox.add(v - fMaxMtnRadius); bbox.add(v + fMaxMtnRadius); if (bbox.getWidth() <= nMaxWidth && bbox.getHeight() <= nMaxHeight) { bbox0 = bbox; _seeds.push_back(v); } else bbox = bbox0; } vrange += vdir * RANDf(fPeakStep); vdir = Matrix3D ::rotateZ(RANDf(_fRotateThreshold) - _fRotateThreshold / 2) * vdir; } bbox.add( VectorTag< float >::V2( bbox.left + static_cast< float > ( mars::MidpointDisplacementGrid ::dimensionFor(static_cast< unsigned short > (bbox.getWidth()), mars::Normal) ), bbox.top + static_cast< float > ( mars::MidpointDisplacementGrid ::dimensionFor(static_cast< unsigned short > (bbox.getHeight()), mars::Normal) ) ) ); _bbox = static_cast< BBox< long > > (bbox) + _ptOrigin; const GWSurfacePos ptsOffset = _ptOrigin - _bbox.getMinimum(); const VectorTag< float > ::V3 ptOffset ( static_cast< float > (ptsOffset.x), static_cast< float > (ptsOffset.y), 0 ); for (MountainGM::SeedList::iterator i = _seeds.begin(); i != _seeds.end(); ++i) *i += ptOffset; } void MountainGM::doMorph (LockedGWSection & section) const { using namespace mars; mars::ptr< mars::MidpointDisplacementGrid > pMPDG = MidpointDisplacementGrid ::createInstance ( static_cast< unsigned short > (_bbox.getWidth()), static_cast< unsigned short > (_bbox.getHeight()), mars::Normal ); const unsigned short nSeedIterations = static_cast< unsigned short > (mars::RNDi(static_cast< float > (pMPDG->itercount) * _fSeedIterations)); const int nStride = pMPDG->mindim - 1; const GWSurfacePos ptgwspOffset = _ptOrigin - _bbox.getMinimum(); // Initialize iteration count const LockedGWSection::View * pView = section.createView(_bbox.left, _bbox.top, _bbox.right, _bbox.bottom); MidpointDisplacementGrid ::PinkFn funcDefault(pMPDG->mindim, _fCoarseness); funcDefault.iteration(pMPDG->stride(), 0); pMPDG->copyEdges(mars::StitchTop | mars::StitchLeft | mars::StitchBottom | mars::StitchRight, pView->getHeightMap()); SeedModifiedMidpointDisplacement< MidpointDisplacementGrid< GeoHeightMap::Precision >::PinkFn > funcSeeding (_fSeedObedience, _nMntRangeWidth, _fCoarseness, _fFalloff); MidpointDisplacementGrid< GeoHeightMap::Precision >::PinkFn funcNoisy (_nMntRangeWidth, _fCoarseness); funcSeeding.init(pView, _seeds); pMPDG->iterations(nSeedIterations, funcSeeding); pMPDG->iterations(pMPDG->itercount - nSeedIterations, funcNoisy, mars::StitchBottom | mars::StitchTop | mars::StitchRight | mars::StitchLeft); DEMDUMP << *pMPDG; section.at(_bbox.left, _bbox.top) << *pMPDG; section.releaseView(pView); } mars::ptr< PersistentGeoMorph::State > MountainGM::createState() const { MtnState * pState = new MtnState(); pState->seeds = _seeds; pState->nNumMountains = _nNumMountains; pState->fSeedObedience = _fSeedObedience; pState->fSeedIterations = _fSeedIterations; pState->fRotateThreshold = _fRotateThreshold; pState->fPeakHeightFluxPct = _fPeakHeightFluxPct; pState->fCoarseness = _fCoarseness; pState->fFalloff = _fFalloff; pState->fClumpMaxDisplace = _fClumpMaxDisplace; pState->nMntRangeWidth = _nMntRangeWidth; pState->nMntRangeAvgHeight = _nMntRangeAvgHeight; pState->ptOrigin = _ptOrigin; pState->mmnClumpSize = _mmnClumpSize; pState->bbox = _bbox; return pState; } MountainGM::MountainGM( const MtnState & state ) : _ptOrigin(state.ptOrigin), _fSeedObedience(state.fSeedObedience), _fSeedIterations(state.fSeedIterations), _nMntRangeWidth(state.nMntRangeWidth), _nMntRangeAvgHeight(state.nMntRangeAvgHeight), _mmnClumpSize(state.mmnClumpSize), _fClumpMaxDisplace(state.fClumpMaxDisplace), _nNumMountains(state.nNumMountains), _fRotateThreshold(state.fRotateThreshold), _fPeakHeightFluxPct (state.fPeakHeightFluxPct), _fCoarseness(state.fCoarseness), _fFalloff(state.fFalloff), _bbox(state.bbox) { } MountainGM::MountainGM( const GWSurfacePos & ptOrigin, const float fSeedObedience, const float fSeedIterations, const unsigned short nMntRangeWidth, const unsigned short nMntRangeAvgHeight, const mars::RangeX< unsigned short > mmnClumpSize, const float fClumpMaxDisplace, const unsigned short nNumMountains, const float fRotateThreshold, const float frPeakHeightFlux, const float frCoarseness, const float fFalloff ) : _ptOrigin(ptOrigin), _fSeedObedience(fSeedObedience), _fSeedIterations(fSeedIterations), _nMntRangeWidth(nMntRangeWidth), _nMntRangeAvgHeight(nMntRangeAvgHeight), _mmnClumpSize(mmnClumpSize), _fClumpMaxDisplace(fClumpMaxDisplace), _nNumMountains(nNumMountains), _fRotateThreshold(fRotateThreshold), _fPeakHeightFluxPct (frPeakHeightFlux), _fCoarseness(frCoarseness), _fFalloff(fFalloff) { } GeoMorph * MountainGMFactory::createRandomInstance( const long x, const long y ) const { return new MountainGM( GWSurfacePos(x, y), _fSeedObedience, _mmfSeedIterations.next(), _mmnRangeWidth.next(), _mmnPeakAvgHeight.next(), _mmnClumpSize, _fClumpMaxDisplace, _mmnNumMountains.next(), _mmfRotateThreshold.next(), _frPeakHeightFlux, _fCoarseness, _fFalloff ); } void MountainGMFactory::configure( IConfigGMFactory * pFactoryConfig, const IConfigGMFactory::Settings & settings ) { const geoworld::IConfigSection & section = *pFactoryConfig->getSection(); *section["seed_adherence"] >> _fSeedObedience; *section["seed_iterations"] >> _mmfSeedIterations; *section["num_peaks"] >> _mmnNumMountains; *section["clump_size"] >> _mmnClumpSize; *section["clump_displace"] >> _fClumpMaxDisplace; *section["non_straightness"] >> _mmfRotateThreshold; *section["range_width"] >> _mmnRangeWidth; *section["height_flux"] >> _frPeakHeightFlux; *section["peak_height"] >> _mmnPeakAvgHeight; *section["coarseness"] >> _fCoarseness; *section["falloff"] >> _fFalloff; } void MountainGMFactory::save( const GeoMorph * pGM, mars::ObjectStream & outs ) const { const MountainGM * pMGM = dynamic_cast< const MountainGM * > (pGM); assert(pMGM != NULL); *(pMGM->createState()) >> outs; } const GeoMorph * MountainGMFactory::restore( mars::ObjectStream & ins ) const { MountainGM::MtnState state; state << ins; return new MountainGM(state); } mars::ObjectStream & MountainGM::MtnState::operator >> ( mars::ObjectStream & outs ) const { outs << seeds << nNumMountains << fSeedObedience << fSeedIterations << fRotateThreshold << fPeakHeightFluxPct << fCoarseness << fFalloff << fClumpMaxDisplace << nMntRangeWidth << nMntRangeAvgHeight << ptOrigin << mmnClumpSize << bbox; return outs; } mars::ObjectStream & MountainGM::MtnState::operator << ( mars::ObjectStream & ins ) { ins >> seeds >> nNumMountains >> fSeedObedience >> fSeedIterations >> fRotateThreshold >> fPeakHeightFluxPct >> fCoarseness >> fFalloff >> fClumpMaxDisplace >> nMntRangeWidth >> nMntRangeAvgHeight >> ptOrigin >> mmnClumpSize >> bbox; return ins; } }