/// \brief One dimensional midpoint displacement fractal. /// /// Size must be a power of 2. /// Falloff is the decay of displacement as the fractal is refined. /// Array is size + 1 long. array[0] and array[size] are filled /// with the control points for the fractal. void Segment::fill1d(BasePoint const & l, BasePoint const &h, float *array) const { array[0] = l.height(); array[m_res] = h.height(); LinInterp li(m_res, l.roughness(), h.roughness()); // seed the RNG. // The RNG is seeded only once for the line and the seed is based on the // two endpoints -because they are the common parameters for two adjoining // tiles //srand((l.seed() * 1000 + h.seed())); WFMath::MTRand::uint32 seed[2] = { l.seed(), h.seed() }; WFMath::MTRand rng(seed, 2); // stride is used to step across the array in a deterministic fashion // effectively we do the 1/2 point, then the 1/4 points, then the 1/8th // points etc. this has to be the same order every time because we call // on the RNG at every point decltype(getResolution()) stride = m_res / 2; // depth is used to indicate what level we are on. the displacement is // reduced each time we traverse the array. float depth = 1; while (stride) { for (decltype(getResolution()) i = stride; i < m_res; i += stride * 2) { auto hh = array[i - stride]; auto lh = array[i + stride]; auto hd = std::fabs(hh - lh); auto roughness = li.calc(i); //eliminate the problem where hd is nearly zero, leaving a flat section. if ((hd * 100.f) < roughness) { hd += 0.05f * roughness; } array[i] = ((hh + lh) / 2.f) + randHalf(rng) * roughness * hd / (1.f + std::pow(depth, BasePoint::FALLOFF)); } stride >>= 1; depth++; } }
/// \brief Two dimensional midpoint displacement fractal. /// /// For a tile where edges are to be filled by 1d fractals. /// Size must be a power of 2, array is (size + 1) * (size + 1) with the /// corners the control points. void Segment::fill2d(const BasePoint& p1, const BasePoint& p2, const BasePoint& p3, const BasePoint& p4) { assert(m_points!=0); // int line = m_res+1; // calculate the edges first. This is necessary so that segments tile // seamlessly note the order in which the edges are calculated and the // direction. opposite edges are calculated the same way (eg left->right) // so that the top of one tile matches the bottom of another, likewise // with sides. // temporary array used to hold each edge float * edge = new float[m_size]; // calc top edge and copy into m_points fill1d(p1,p2,edge); for (int i=0;i<=m_res;i++) { m_points[0*m_size + i] = edge[i]; checkMaxMin(edge[i]); } // calc left edge and copy into m_points fill1d(p1,p4,edge); for (int i=0;i<=m_res;i++) { m_points[i*m_size + 0] = edge[i]; checkMaxMin(edge[i]); } // calc right edge and copy into m_points fill1d(p2,p3,edge); for (int i=0;i<=m_res;i++) { m_points[i*m_size + m_res] = edge[i]; checkMaxMin(edge[i]); } // calc bottom edge and copy into m_points fill1d(p4,p3,edge); for (int i=0;i<=m_res;i++) { m_points[m_res*m_size + i] = edge[i]; checkMaxMin(edge[i]); } // seed the RNG - this is the 5th and last seeding for the tile. // it was seeded once for each edge, now once for the tile. //srand(p1.seed()*20 + p2.seed()*15 + p3.seed()*10 + p4.seed()*5); WFMath::MTRand::uint32 seed[4]={ p1.seed(), p2.seed(), p3.seed(), p4.seed() }; WFMath::MTRand rng(seed, 4); QuadInterp qi(m_res, p1.roughness(), p2.roughness(), p3.roughness(), p4.roughness()); float f = BasePoint::FALLOFF; float depth=0; // center of m_points is done separately int stride = m_res/2; //float roughness = (p1.roughness+p2.roughness+p3.roughness+p4.roughness)/(4.0f); float roughness = qi.calc(stride, stride); m_points[stride*m_size + stride] = qRMD(rng, m_points[0 * m_size + stride], m_points[stride*m_size + 0], m_points[stride*m_size + m_res], m_points[m_res*m_size + stride], roughness, f, depth); checkMaxMin(m_points[stride*m_size + stride]); stride >>= 1; // skip across the m_points and fill in the points // alternate cross and plus shapes. // this is a diamond-square algorithm. while (stride) { //Cross shape - + contributes to value at X //+ . + //. X . //+ . + for (int i=stride;i<m_res;i+=stride*2) { for (int j=stride;j<m_res;j+=stride*2) { roughness=qi.calc(i,j); m_points[j*m_size + i] = qRMD(rng, m_points[(i-stride) + (j+stride) * (m_size)], m_points[(i+stride) + (j-stride) * (m_size)], m_points[(i+stride) + (j+stride) * (m_size)], m_points[(i-stride) + (j-stride) * (m_size)], roughness, f, depth); checkMaxMin(m_points[j*m_size + i]); } } depth++; //Plus shape - + contributes to value at X //. + . //+ X + //. + . for (int i=stride*2;i<m_res;i+=stride*2) { for (int j=stride;j<m_res;j+=stride*2) { roughness=qi.calc(i,j); m_points[j*m_size + i] = qRMD(rng, m_points[(i-stride) + (j) * (m_size)], m_points[(i+stride) + (j) * (m_size)], m_points[(i) + (j+stride) * (m_size)], m_points[(i) + (j-stride) * (m_size)], roughness, f , depth); checkMaxMin(m_points[j*m_size + i]); } } for (int i=stride;i<m_res;i+=stride*2) { for (int j=stride*2;j<m_res;j+=stride*2) { roughness=qi.calc(i,j); m_points[j*m_size + i] = qRMD(rng, m_points[(i-stride) + (j) * (m_size)], m_points[(i+stride) + (j) * (m_size)], m_points[(i) + (j+stride) * (m_size)], m_points[(i) + (j-stride) * (m_size)], roughness, f, depth); checkMaxMin(m_points[j*m_size + i]); } } stride>>=1; depth++; } delete [] edge; }