Fix parallax and space dust jitter when lerping zoom level on very old universes
This commit is contained in:
parent
0c74c70475
commit
7ff3e0fecc
@ -13,34 +13,36 @@ namespace Star {
|
|||||||
// predictable and uses the RandomSource in a predictable way. Useful for
|
// predictable and uses the RandomSource in a predictable way. Useful for
|
||||||
// things like starfields, fields of debris, random object placement, etc.
|
// things like starfields, fields of debris, random object placement, etc.
|
||||||
|
|
||||||
template <typename PointData>
|
template <typename PointData, typename DataType = float>
|
||||||
class Random2dPointGenerator {
|
class Random2dPointGenerator {
|
||||||
public:
|
public:
|
||||||
typedef List<pair<Vec2F, PointData>> PointSet;
|
typedef Star::Polygon<DataType> Poly;
|
||||||
|
typedef Star::Vector<DataType, 2> Point;
|
||||||
|
typedef Star::Rect<DataType> Rect;
|
||||||
|
typedef List<pair<Point, PointData>> PointSet;
|
||||||
Random2dPointGenerator(uint64_t seed, float cellSize, Vec2I const& densityRange);
|
Random2dPointGenerator(uint64_t seed, float cellSize, Vec2I const& densityRange);
|
||||||
|
|
||||||
// Each point will in the area will be generated in a predictable order, and
|
// Each point will in the area will be generated in a predictable order, and
|
||||||
// if the callback uses the RandomSource in a predictable way, will generate
|
// if the callback uses the RandomSource in a predictable way, will generate
|
||||||
// the same field for every call.
|
// the same field for every call.
|
||||||
template <typename PointCallback>
|
template <typename PointCallback>
|
||||||
PointSet generate(PolyF const& area, PointCallback callback);
|
PointSet generate(Poly const& area, PointCallback callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HashTtlCache<Vec2F, PointSet> m_cache;
|
HashTtlCache<Point, PointSet> m_cache;
|
||||||
|
|
||||||
uint64_t m_seed;
|
uint64_t m_seed;
|
||||||
float m_cellSize;
|
float m_cellSize;
|
||||||
Vec2I m_densityRange;
|
Vec2I m_densityRange;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename PointData>
|
template <typename PointData, typename DataType>
|
||||||
inline Random2dPointGenerator<PointData>::Random2dPointGenerator(uint64_t seed, float cellSize, Vec2I const& densityRange)
|
inline Random2dPointGenerator<PointData, DataType>::Random2dPointGenerator(uint64_t seed, float cellSize, Vec2I const& densityRange)
|
||||||
: m_seed(seed), m_cellSize(cellSize), m_densityRange(densityRange) {}
|
: m_seed(seed), m_cellSize(cellSize), m_densityRange(densityRange) {}
|
||||||
|
|
||||||
template <typename PointData>
|
template <typename PointData, typename DataType>
|
||||||
template <typename PointCallback>
|
template <typename PointCallback>
|
||||||
auto Random2dPointGenerator<PointData>::generate(PolyF const& area, PointCallback callback) -> PointSet {
|
auto Random2dPointGenerator<PointData, DataType>::generate(Poly const& area, PointCallback callback) -> PointSet {
|
||||||
auto bound = area.boundBox();
|
auto bound = area.boundBox();
|
||||||
int64_t sectorXMin = std::floor(bound.xMin() / m_cellSize);
|
int64_t sectorXMin = std::floor(bound.xMin() / m_cellSize);
|
||||||
int64_t sectorYMin = std::floor(bound.yMin() / m_cellSize);
|
int64_t sectorYMin = std::floor(bound.yMin() / m_cellSize);
|
||||||
@ -48,22 +50,21 @@ auto Random2dPointGenerator<PointData>::generate(PolyF const& area, PointCallbac
|
|||||||
int64_t sectorYMax = std::ceil(bound.yMax() / m_cellSize);
|
int64_t sectorYMax = std::ceil(bound.yMax() / m_cellSize);
|
||||||
|
|
||||||
PointSet finalResult;
|
PointSet finalResult;
|
||||||
|
RandomSource sectorRandomness;
|
||||||
|
|
||||||
for (int64_t x = sectorXMin; x <= sectorXMax; ++x) {
|
for (int64_t x = sectorXMin; x <= sectorXMax; ++x) {
|
||||||
for (int64_t y = sectorYMin; y <= sectorYMax; ++y) {
|
for (int64_t y = sectorYMin; y <= sectorYMax; ++y) {
|
||||||
auto sector = RectF::withSize({x * m_cellSize, y * m_cellSize}, Vec2F::filled(m_cellSize));
|
auto sector = Rect::withSize({x * m_cellSize, y * m_cellSize}, Point::filled(m_cellSize));
|
||||||
if (!area.intersects(PolyF(sector)))
|
if (!area.intersects(Poly(sector)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
finalResult.appendAll(m_cache.get(Vec2F(x, y), [&](Vec2F const&) {
|
finalResult.appendAll(m_cache.get(Point(x, y), [&](Point const&) {
|
||||||
PointSet sectorResult;
|
PointSet sectorResult;
|
||||||
|
sectorRandomness.init(staticRandomU64(m_seed, x, y));
|
||||||
RandomSource sectorRandomness(staticRandomU64(m_seed, x, y));
|
|
||||||
|
|
||||||
unsigned max = sectorRandomness.randInt(m_densityRange[0], m_densityRange[1]);
|
unsigned max = sectorRandomness.randInt(m_densityRange[0], m_densityRange[1]);
|
||||||
for (unsigned i = 0; i < max; ++i) {
|
for (unsigned i = 0; i < max; ++i) {
|
||||||
Vec2F pointPos = Vec2F(x + sectorRandomness.randf(), y + sectorRandomness.randf()) * m_cellSize;
|
Point pointPos = Point(x + (DataType)sectorRandomness.randd(), y + (DataType)sectorRandomness.randd()) * m_cellSize;
|
||||||
sectorResult.append(pair<Vec2F, PointData>(pointPos, callback(sectorRandomness)));
|
sectorResult.append(pair<Point, PointData>(pointPos, callback(sectorRandomness)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return sectorResult;
|
return sectorResult;
|
||||||
|
@ -100,7 +100,7 @@ void EnvironmentPainter::renderDebrisFields(float pixelRatio, Vec2F const& scree
|
|||||||
if (sky.type == SkyType::Orbital || sky.type == SkyType::Warp) {
|
if (sky.type == SkyType::Orbital || sky.type == SkyType::Warp) {
|
||||||
Vec2F viewSize = screenSize / pixelRatio;
|
Vec2F viewSize = screenSize / pixelRatio;
|
||||||
Vec2F viewCenter = viewSize / 2;
|
Vec2F viewCenter = viewSize / 2;
|
||||||
Vec2F viewMin = sky.starOffset - viewCenter;
|
Vec2D viewMin = Vec2D(sky.starOffset - viewCenter);
|
||||||
|
|
||||||
Mat3F rotMatrix = Mat3F::rotation(sky.starRotation, viewCenter);
|
Mat3F rotMatrix = Mat3F::rotation(sky.starRotation, viewCenter);
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ void EnvironmentPainter::renderDebrisFields(float pixelRatio, Vec2F const& scree
|
|||||||
float debrisYVel = staticRandomFloatRange(spaceDebrisVelocityRange[0], spaceDebrisVelocityRange[1], sky.skyParameters.seed, i, "DebrisFieldYVel");
|
float debrisYVel = staticRandomFloatRange(spaceDebrisVelocityRange[0], spaceDebrisVelocityRange[1], sky.skyParameters.seed, i, "DebrisFieldYVel");
|
||||||
|
|
||||||
// Translate the entire field to make the debris seem as though they are moving
|
// Translate the entire field to make the debris seem as though they are moving
|
||||||
Vec2F velocityOffset = -Vec2F(debrisXVel, debrisYVel) * sky.epochTime;
|
Vec2D velocityOffset = -Vec2D(debrisXVel, debrisYVel) * sky.epochTime;
|
||||||
|
|
||||||
JsonArray imageOptions = debrisField.query("list").toArray();
|
JsonArray imageOptions = debrisField.query("list").toArray();
|
||||||
Vec2U biggest = Vec2U();
|
Vec2U biggest = Vec2U();
|
||||||
@ -123,10 +123,8 @@ void EnvironmentPainter::renderDebrisFields(float pixelRatio, Vec2F const& scree
|
|||||||
}
|
}
|
||||||
|
|
||||||
float screenBuffer = ceil((float)biggest.max() * (float)Constants::sqrt2);
|
float screenBuffer = ceil((float)biggest.max() * (float)Constants::sqrt2);
|
||||||
PolyF field = PolyF(RectF::withSize(viewMin + velocityOffset, viewSize).padded(screenBuffer));
|
PolyD field = PolyD(RectD::withSize(viewMin + velocityOffset, Vec2D(viewSize)).padded(screenBuffer));
|
||||||
|
|
||||||
Vec2F debrisAngularVelocityRange = jsonToVec2F(debrisField.query("angularVelocityRange"));
|
Vec2F debrisAngularVelocityRange = jsonToVec2F(debrisField.query("angularVelocityRange"));
|
||||||
|
|
||||||
auto debrisItems = m_debrisGenerators[i]->generate(field,
|
auto debrisItems = m_debrisGenerators[i]->generate(field,
|
||||||
[&](RandomSource& rand) {
|
[&](RandomSource& rand) {
|
||||||
StringView debrisImage = *rand.randFrom(imageOptions).stringPtr();
|
StringView debrisImage = *rand.randFrom(imageOptions).stringPtr();
|
||||||
@ -135,10 +133,10 @@ void EnvironmentPainter::renderDebrisFields(float pixelRatio, Vec2F const& scree
|
|||||||
return pair<StringView, float>(debrisImage, debrisAngularVelocity);
|
return pair<StringView, float>(debrisImage, debrisAngularVelocity);
|
||||||
});
|
});
|
||||||
|
|
||||||
Vec2F debrisPositionOffset = viewMin + velocityOffset;
|
Vec2D debrisPositionOffset = viewMin + velocityOffset;
|
||||||
|
|
||||||
for (auto& debrisItem : debrisItems) {
|
for (auto& debrisItem : debrisItems) {
|
||||||
Vec2F debrisPosition = rotMatrix.transformVec2(debrisItem.first - debrisPositionOffset);
|
Vec2F debrisPosition = rotMatrix.transformVec2(Vec2F(debrisItem.first - debrisPositionOffset));
|
||||||
float debrisAngle = fmod(Constants::deg2rad * debrisItem.second.second * sky.epochTime, Constants::pi * 2) + sky.starRotation;
|
float debrisAngle = fmod(Constants::deg2rad * debrisItem.second.second * sky.epochTime, Constants::pi * 2) + sky.starRotation;
|
||||||
drawOrbiter(pixelRatio, screenSize, sky, {SkyOrbiterType::SpaceDebris, 1.0f, debrisAngle, debrisItem.second.first, debrisPosition});
|
drawOrbiter(pixelRatio, screenSize, sky, {SkyOrbiterType::SpaceDebris, 1.0f, debrisAngle, debrisItem.second.first, debrisPosition});
|
||||||
}
|
}
|
||||||
@ -259,8 +257,8 @@ void EnvironmentPainter::renderParallaxLayers(
|
|||||||
// texture offset in *screen pixel space*
|
// texture offset in *screen pixel space*
|
||||||
Vec2F parallaxOffset = layer.parallaxOffset * camera.pixelRatio();
|
Vec2F parallaxOffset = layer.parallaxOffset * camera.pixelRatio();
|
||||||
if (layer.speed != 0) {
|
if (layer.speed != 0) {
|
||||||
double drift = fmod((double)layer.speed * (sky.epochTime / (double)sky.dayLength) * camera.pixelRatio(), (double)parallaxPixels[0]);
|
double drift = fmod((double)layer.speed * (sky.epochTime / (double)sky.dayLength), (double)parallaxSize[0]);
|
||||||
parallaxOffset[0] = fmod(parallaxOffset[0] + drift, parallaxPixels[0]);
|
parallaxOffset[0] = fmod(parallaxOffset[0] + drift * camera.pixelRatio(), parallaxPixels[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// parallax camera world position in *parallax space*
|
// parallax camera world position in *parallax space*
|
||||||
@ -467,7 +465,7 @@ void EnvironmentPainter::setupStars(SkyRenderData const& sky) {
|
|||||||
int debrisCellSize = debrisFields[i].getInt("cellSize");
|
int debrisCellSize = debrisFields[i].getInt("cellSize");
|
||||||
Vec2I debrisCountRange = jsonToVec2I(debrisFields[i].get("cellCountRange"));
|
Vec2I debrisCountRange = jsonToVec2I(debrisFields[i].get("cellCountRange"));
|
||||||
uint64_t debrisSeed = staticRandomU64(sky.skyParameters.seed, i, "DebrisFieldSeed");
|
uint64_t debrisSeed = staticRandomU64(sky.skyParameters.seed, i, "DebrisFieldSeed");
|
||||||
m_debrisGenerators[i] = make_shared<Random2dPointGenerator<pair<String, float>>>(debrisSeed, debrisCellSize, debrisCountRange);
|
m_debrisGenerators[i] = make_shared<Random2dPointGenerator<pair<String, float>, double>>(debrisSeed, debrisCellSize, debrisCountRange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ private:
|
|||||||
uint64_t m_starsHash;
|
uint64_t m_starsHash;
|
||||||
List<TexturePtr> m_starTextures;
|
List<TexturePtr> m_starTextures;
|
||||||
shared_ptr<Random2dPointGenerator<pair<size_t, float>>> m_starGenerator;
|
shared_ptr<Random2dPointGenerator<pair<size_t, float>>> m_starGenerator;
|
||||||
List<shared_ptr<Random2dPointGenerator<pair<String, float>>>> m_debrisGenerators;
|
List<shared_ptr<Random2dPointGenerator<pair<String, float>, double>>> m_debrisGenerators;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user