#pragma once // system #include // external #include #include // vendor #include #include #include struct Impact { glm::vec3 pos; float r; bool contains(const glm::vec3 &p) const { return (glm::length(pos - p) < r); } }; class Density { std::vector impacts; RTree impactTree; siv::PerlinNoise noise; glm::vec3 scale; float scaleFactor; float frequency, octave; size_t version; public: Density() : scale(1.0f), scaleFactor(4), frequency(2), octave(2), version(0) { } const std::vector &getImpacts() { return impacts; } size_t getVersion() { return version; } void addImpact(Impact &impact) { size_t idx = impacts.size(); impacts.push_back(impact); glm::vec3 l = impact.pos - glm::vec3(impact.r); glm::vec3 u = impact.pos + glm::vec3(impact.r); impactTree.Insert(&l.x, &u.x, idx); version++; } void setSeed(uint32_t seed) { noise.reseed(seed); version++; impacts.clear(); } void setScale(const glm::vec3 &scale) { this->scale = scale; scaleFactor = 4.f / std::min(scale.x, std::min(scale.y, scale.z)); impacts.clear(); version++; } void setFrequency(float f) { frequency = f; impacts.clear(); version++; } void setOctave(float o) { octave = o; impacts.clear(); version++; } float operator()(const glm::vec3 &p, bool includeImpacts = true) const { if (includeImpacts && impacts.size() > 0) { bool insideImpact = false; impactTree.Search(&p.x, &p.x, [&](int idx) -> bool { if (impacts[idx].contains(p)) { insideImpact = true; return false; } else { return true; } }); if (insideImpact) return 0.f; } float v = (float) noise.octaveNoise0_1(p.x * frequency, p.y * frequency, p.z * frequency, octave); glm::vec3 r = p - glm::vec3(0.5); v *= std::max(1.f - scaleFactor * dot(scale * r, r), 0.f); return v; } bool intersectIsolevel(const glm::vec3 &start, const glm::vec3 &end, glm::vec3 &pos, float isolevel, float resolution) const { glm::vec3 step = glm::normalize(end - start) * glm::vec3(resolution); size_t nSteps = ceil(glm::length(end - start) / resolution); pos = start; for (size_t iStep = 0; iStep < nSteps; iStep++) { if ((*this)(pos) > isolevel) return true; pos += step; } return false; } void addRandomImpacts(size_t count, float isolevel, float resolution, float minRadius, float maxRadius, int exponent) { for (size_t i = 0; i < count; i++) { glm::vec3 start = glm::vec3(0.5f) + glm::sphericalRand(0.6f); glm::vec3 end = glm::vec3(0.5f) + glm::sphericalRand(0.6f); Impact impact; if (intersectIsolevel(start, end, impact.pos, isolevel, resolution)) { impact.pos -= (impact.r / 1.5f) * normalize(end-start); impact.r = minRadius + pow(glm::linearRand(0.f, 1.f), exponent) * maxRadius; addImpact(impact); } } } void saveCrossSection(const char *filename, size_t pixels) { float step = 1.f / pixels; glm::vec3 pos(step / 2, step / 2, 0.5f); std::vector img(pixels * pixels); for (size_t ix = 0; ix < pixels; ix++) { for (size_t iy = 0; iy < pixels; iy++) { glm::vec3 pos(step * (0.5f + ix), step * (0.5f + iy), 0.5f); img[iy * pixels + ix] = (*this)(pos) * 255; } } stbi_write_png(filename, pixels, pixels, 1, img.data(), 0); } };