From 35cc9ae4b7de8a1669e68101536c5fdd16845cee Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 9 Sep 2026 20:05:30 +0200 Subject: [PATCH] feat: extend TileTexture --- core/src/TileTexture.cpp | 126 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/core/src/TileTexture.cpp b/core/src/TileTexture.cpp index be6e80f..c7cb011 100644 --- a/core/src/TileTexture.cpp +++ b/core/src/TileTexture.cpp @@ -458,3 +458,129 @@ float materialGrain(const TileSpec& spec, int tileSalt, int x, int y, int size, float amount = spec.noise; if (amount <= 0.0f) return 1.0f; + float shape = 0.0f; + switch (spec.material) { + case TileMaterial::Stone: { + float speck = gridValue(tileSalt, x, y, 101u, seed) * 2.0f - 1.0f; + float mottle = + fractalNoise(tileSalt, x * 4.0f / size, y * 4.0f / size, 103u, seed) * 2.0f - 1.0f; + shape = speck * 0.70f + mottle * 0.55f; + break; + } + case TileMaterial::Soil: { + float clump = + fractalNoise(tileSalt, x * 5.0f / size, y * 5.0f / size, 107u, seed) * 2.0f - 1.0f; + float crumb = gridValue(tileSalt, x, y, 109u, seed) * 2.0f - 1.0f; + shape = clump * 0.90f + crumb * 0.45f; + break; + } + case TileMaterial::Wood: { + float across = spec.vertical ? static_cast(x) : static_cast(y); + float along = spec.vertical ? static_cast(y) : static_cast(x); + float ring = lineNoise(tileSalt, across * 6.0f / size, 113u, seed) * 2.0f - 1.0f; + float fibre = + valueNoise(tileSalt, along * 3.0f / size, across * 9.0f / size, 127u, seed) * 2.0f - + 1.0f; + shape = ring * 0.65f + fibre * 0.55f; + break; + } + case TileMaterial::Foliage: { + float cluster = fractalNoise(tileSalt, x * 6.0f / size, y * 6.0f / size, 131u, seed); + float fleck = gridValue(tileSalt, x, y, 137u, seed) * 2.0f - 1.0f; + shape = (cluster < 0.44f ? -0.95f : 0.40f) + fleck * 0.45f; + break; + } + case TileMaterial::Metal: { + float sheen = std::cos((x + y) * 0.5f / size * twoPi) * 0.85f; + float scratch = gridValue(tileSalt, x, y * 3, 139u, seed) > 0.88f ? -0.95f : 0.0f; + shape = sheen + scratch + (gridValue(tileSalt, x, y, 149u, seed) * 2.0f - 1.0f) * 0.25f; + break; + } + case TileMaterial::Crystal: { + float facet = + valueNoise(tileSalt, (x + y) * 2.5f / size, (x - y) * 2.5f / size, 151u, seed); + shape = (facet - 0.5f) * 2.4f; + break; + } + case TileMaterial::Fabric: { + float weave = ((x / 2) + (y / 2)) % 2 == 0 ? 0.75f : -0.75f; + shape = weave + (gridValue(tileSalt, x, y, 157u, seed) * 2.0f - 1.0f) * 0.40f; + break; + } + case TileMaterial::Fluid: { + float swell = std::sin((y * 2.0f / size + x * 0.35f / size) * twoPi); + shape = swell * 0.80f + (gridValue(tileSalt, x, y, 163u, seed) * 2.0f - 1.0f) * 0.35f; + break; + } + case TileMaterial::Light: { + float half = size * 0.5f; + float dx = (x + 0.5f - half) / half; + float dy = (y + 0.5f - half) / half; + float glow = 1.0f - clamp01(std::sqrt(dx * dx + dy * dy)); + shape = glow * 1.7f - 0.65f + (gridValue(tileSalt, x, y, 167u, seed) * 2.0f - 1.0f) * 0.3f; + break; + } + case TileMaterial::Organic: { + float soft = fractalNoise(tileSalt, x * 3.0f / size, y * 3.0f / size, 173u, seed); + float pore = gridValue(tileSalt, x, y, 179u, seed); + shape = (soft - 0.5f) * 1.9f + (pore > 0.92f ? -0.85f : 0.0f); + break; + } + case TileMaterial::Count: break; + } + return std::max(0.0f, 1.0f + amount * shape); +} + +} // namespace + +TileSampler::TileSampler(const TileTextureParams& params) : settings(params) {} + +TileSampler::TileSampler(const TileTextureParams& params, const TilePalette& palette) + : settings(params), table(palette) {} + +TilePixel TileSampler::pixelOf(const TileSpec& spec, int tileSalt, int x, int y) const { + int size = settings.tileSize; + PatternSample pattern = samplePattern(spec, tileSalt, x, y, size, settings.noiseSeed); + TileRgb blended = mixRgb(spec.base, spec.accent, pattern.accentWeight); + + float brightness = pattern.shade * materialGrain(spec, tileSalt, x, y, size, settings.noiseSeed); + // The old atlas darkened the first row and column so block edges stayed + // legible against each other. Cheap, and every tile still needs it. + if (x == 0 || y == 0) brightness *= 0.86f; + + float red = blended.r * brightness; + float green = blended.g * brightness; + float blue = blended.b * brightness; + + red = spec.base.r + (red - spec.base.r) * settings.contrast; + green = spec.base.g + (green - spec.base.g) * settings.contrast; + blue = spec.base.b + (blue - spec.base.b) * settings.contrast; + + TilePixel out; + out.r = toByte(red); + out.g = toByte(green); + out.b = toByte(blue); + out.a = (spec.cutout && pattern.hole) ? 0 : 255; + return out; +} + +TilePixel TileSampler::pixelAt(int tile, int x, int y) const { + return pixelOf(table.specFor(tile), tile, x, y); +} + +void TileSampler::writeTile(int tile, uint8_t* rgba) const { + const TileSpec& spec = table.specFor(tile); + int size = settings.tileSize; + for (int y = 0; y < size; ++y) { + for (int x = 0; x < size; ++x) { + TilePixel texel = pixelOf(spec, tile, x, y); + std::size_t offset = (static_cast(y) * size + x) * 4; + rgba[offset + 0] = texel.r; + rgba[offset + 1] = texel.g; + rgba[offset + 2] = texel.b; + rgba[offset + 3] = texel.a; + } + } +} + +} // namespace vf