Skip to content

Commit 16ba2bc

Browse files
author
LoneWandererProductions
committed
add 3 new textures
1 parent 23a3c9b commit 16ba2bc

4 files changed

Lines changed: 342 additions & 20 deletions

File tree

‎Imaging.Texture.Tests/TextureMathEngineTests.cs‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,39 @@ public void GenerateDesertSandFlat_VisualTest()
462462
SaveBufferToImage(buffer, "34_DesertSand_Flat.png");
463463
}
464464

465+
/// <summary>
466+
/// Generates the concrete porous visual test.
467+
/// </summary>
468+
[TestMethod]
469+
public void GenerateConcretePorous_VisualTest()
470+
{
471+
// Architectural matte concrete with fine aggregate pores and no sine-wave ring artifacts
472+
var buffer = TextureMathEngine.GenerateConcretePorous(TestWidth, TestHeight, _noiseGenerator, baseTone: 140, gritSpread: 22);
473+
SaveBufferToImage(buffer, "04b_Concrete_Porous.png");
474+
}
475+
476+
/// <summary>
477+
/// Generates the stylized foliage visual test.
478+
/// </summary>
479+
[TestMethod]
480+
public void GenerateStylizedFoliage_VisualTest()
481+
{
482+
// Clean, stylized leaf clumps with organic alpha cutouts for meshes and terrain
483+
var buffer = TextureMathEngine.GenerateStylizedFoliage(TestWidth, TestHeight, _noiseGenerator, clusterSize: 32, transparentGaps: true);
484+
SaveBufferToImage(buffer, "13b_Foliage_Stylized.png");
485+
}
486+
487+
/// <summary>
488+
/// Generates the cobblestone beveled visual test.
489+
/// </summary>
490+
[TestMethod]
491+
public void GenerateCobblestoneBeveled_VisualTest()
492+
{
493+
// Flat-top cobblestones with narrow grout seams instead of spherical bubbles
494+
var buffer = TextureMathEngine.GenerateCobblestoneBeveled(TestWidth, TestHeight, cellSize: 28);
495+
SaveBufferToImage(buffer, "18b_Cobblestone_Beveled.png");
496+
}
497+
465498
/// <summary>
466499
/// Converts the RawTextureBuffer span (BGRA) into a standard PNG file.
467500
/// </summary>
@@ -496,4 +529,4 @@ private void SaveBufferToImage(RawTextureBuffer? buffer, string filename)
496529
Trace.WriteLine($"Saved: {filePath}");
497530
}
498531
}
499-
}
532+
}

‎Imaging.Texture/TextureConstants.cs‎

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,14 @@ public static TextureConfig GetStoneConfig()
3030
{
3131
return new()
3232
{
33-
// Reusing this property for the Voronoi grid size (e.g., 4 cells across)
34-
VoronoiGridSize = 4,
35-
// Format: Highlight [0-2], Base Stone [3-5], Shadow [6-8], Mortar [9-11]
36-
RgbRamp = [
37-
220,
38-
220,
39-
220, // Bright edge highlight
40-
130,
41-
130,
42-
130, // Base mid-tone
43-
40,
44-
40,
45-
40, // Deep directional shadow
46-
15,
47-
15,
48-
15 // Dark mortar/grout
49-
]
33+
VoronoiGridSize = 6, // Slightly denser distribution avoids massive color patches
34+
// Soft gradient: Highlight [0-2], Mid-tone [3-5], Shadow [6-8], Grout [9-11]
35+
RgbRamp = [
36+
145, 150, 158, // Subtle edge highlight
37+
110, 115, 122, // Base slate grey
38+
80, 85, 92, // Soft directional shadow
39+
55, 58, 64 // Muted stone grout/seam
40+
]
5041
};
5142
}
5243

@@ -648,4 +639,4 @@ public static TextureConfig GetClayConfig()
648639
};
649640
}
650641
}
651-
}
642+
}

‎Imaging.Texture/TextureMathEngine.cs‎

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,54 @@ public static RawTextureBuffer GenerateConcrete(int width,
262262
return buffer;
263263
}
264264

265+
/// <summary>
266+
/// Generates realistic architectural concrete with fine grit, aggregate pores, and no topographic worm-rings.
267+
/// </summary>
268+
/// <param name="width">The width.</param>
269+
/// <param name="height">The height.</param>
270+
/// <param name="noiseGenInstance">The noise gen instance.</param>
271+
/// <param name="baseTone">The base tone.</param>
272+
/// <param name="gritSpread">The grit spread.</param>
273+
/// <returns>A raw texture buffer containing the generated porous concrete texture.</returns>
274+
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
275+
public static RawTextureBuffer GenerateConcretePorous(
276+
int width,
277+
int height,
278+
object noiseGenInstance,
279+
byte baseTone = 140,
280+
byte gritSpread = 22)
281+
{
282+
var buffer = new RawTextureBuffer(width, height);
283+
var span = buffer.AsSpan();
284+
dynamic noiseGen = noiseGenInstance;
285+
286+
var idx = 0;
287+
for (var y = 0; y < height; y++)
288+
{
289+
for (var x = 0; x < width; x++)
290+
{
291+
// 1. Broad smooth macro patches
292+
var macro = (double)noiseGen.SmoothNoise(x / 48.0, y / 48.0);
293+
294+
// 2. Medium concrete aggregate speckles
295+
var mid = (double)noiseGen.SmoothNoise(x / 12.0, y / 12.0) * 0.4;
296+
297+
// 3. Fine surface pores (high frequency)
298+
var micro = ((double)noiseGen.GetNoise(x, y) - 0.5) * 0.35;
299+
300+
var val = Math.Clamp(macro * 0.6 + mid + micro, 0.0, 1.0);
301+
var colorVal = (byte)Math.Clamp(baseTone - gritSpread + (int)(gritSpread * 2.0 * val), 0, 255);
302+
303+
span[idx++] = colorVal; // B
304+
span[idx++] = colorVal; // G
305+
span[idx++] = colorVal; // R
306+
span[idx++] = 255; // A
307+
}
308+
}
309+
310+
return buffer;
311+
}
312+
265313
/// <summary>
266314
/// Generates interlocking woven canvas fabric textures with deterministic line cutoffs.
267315
/// </summary>
@@ -620,6 +668,99 @@ public static RawTextureBuffer GenerateCellular(int width,
620668
return buffer;
621669
}
622670

671+
/// <summary>
672+
/// Generates realistic flat-top cobblestones with defined grout lines instead of spherical bubbles.
673+
/// </summary>
674+
/// <param name="width">The width.</param>
675+
/// <param name="height">The height.</param>
676+
/// <param name="cellSize">Size of the cell.</param>
677+
/// <param name="stoneR">The stone r.</param>
678+
/// <param name="stoneG">The stone g.</param>
679+
/// <param name="stoneB">The stone b.</param>
680+
/// <param name="groutR">The grout r.</param>
681+
/// <param name="groutG">The grout g.</param>
682+
/// <param name="groutB">The grout b.</param>
683+
/// <returns>The generated raw texture buffer.</returns>
684+
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
685+
public static RawTextureBuffer GenerateCobblestoneBeveled(
686+
int width,
687+
int height,
688+
int cellSize = 28,
689+
byte stoneR = 128, byte stoneG = 132, byte stoneB = 138,
690+
byte groutR = 45, byte groutG = 48, byte groutB = 52)
691+
{
692+
var buffer = new RawTextureBuffer(width, height);
693+
var span = buffer.AsSpan();
694+
var rand = new Random(4242);
695+
696+
var gridCols = (width / cellSize) + 2;
697+
var gridRows = (height / cellSize) + 2;
698+
var pointsX = new int[gridCols, gridRows];
699+
var pointsY = new int[gridCols, gridRows];
700+
701+
for (var y = 0; y < gridRows; y++)
702+
{
703+
for (var x = 0; x < gridCols; x++)
704+
{
705+
pointsX[x, y] = (x * cellSize) + rand.Next(2, cellSize - 2);
706+
pointsY[x, y] = (y * cellSize) + rand.Next(2, cellSize - 2);
707+
}
708+
}
709+
710+
var idx = 0;
711+
712+
for (var y = 0; y < height; y++)
713+
{
714+
for (var x = 0; x < width; x++)
715+
{
716+
var cellX = x / cellSize;
717+
var cellY = y / cellSize;
718+
719+
var d1 = double.MaxValue;
720+
var d2 = double.MaxValue;
721+
722+
var startX = Math.Max(0, cellX - 1);
723+
var endX = Math.Min(gridCols - 1, cellX + 1);
724+
var startY = Math.Max(0, cellY - 1);
725+
var endY = Math.Min(gridRows - 1, cellY + 1);
726+
727+
for (var cy = startY; cy <= endY; cy++)
728+
{
729+
for (var cx = startX; cx <= endX; cx++)
730+
{
731+
var dx = x - pointsX[cx, cy];
732+
var dy = y - pointsY[cx, cy];
733+
var dist = Math.Sqrt(dx * dx + dy * dy);
734+
735+
if (dist < d1)
736+
{
737+
d2 = d1;
738+
d1 = dist;
739+
}
740+
else if (dist < d2)
741+
{
742+
d2 = dist;
743+
}
744+
}
745+
}
746+
747+
// F2 - F1 is 0 at the grout border, and large inside the stone
748+
var borderDist = d2 - d1;
749+
750+
// Sharp bevel: Stones stay completely flat until 2.5 pixels to the seam
751+
var stoneFactor = Math.Clamp(borderDist / 3.0, 0.0, 1.0);
752+
stoneFactor = Math.Pow(stoneFactor, 0.35); // Rapid curve to flat plateau
753+
754+
span[idx++] = (byte)(groutB + (stoneB - groutB) * stoneFactor);
755+
span[idx++] = (byte)(groutG + (stoneG - groutG) * stoneFactor);
756+
span[idx++] = (byte)(groutR + (stoneR - groutR) * stoneFactor);
757+
span[idx++] = 255;
758+
}
759+
}
760+
761+
return buffer;
762+
}
763+
623764
/// <summary>
624765
/// Generates a texture by mapping turbulence noise to a predefined array of colors (Color Ramp).
625766
/// Ideal for lava, magic auras, and water.
@@ -1051,6 +1192,143 @@ internal static RawTextureBuffer GenerateFoliage(int width,
10511192
return buffer;
10521193
}
10531194

1195+
/// <summary>
1196+
/// Generates clean, stylized leaf clumps suitable for low-poly tree meshes and terrain overlays.
1197+
/// Leaves lighting to the 3D engine while providing rich organic micro-shapes and clean alpha silhouettes.
1198+
/// </summary>
1199+
/// <param name="width">The width.</param>
1200+
/// <param name="height">The height.</param>
1201+
/// <param name="noiseGenInstance">The noise gen instance.</param>
1202+
/// <param name="clusterSize">Size of the cluster.</param>
1203+
/// <param name="transparentGaps">if set to <c>true</c> [transparent gaps].</param>
1204+
/// <param name="baseR">The base r.</param>
1205+
/// <param name="baseG">The base g.</param>
1206+
/// <param name="baseB">The base b.</param>
1207+
/// <param name="highlightR">The highlight r.</param>
1208+
/// <param name="highlightG">The highlight g.</param>
1209+
/// <param name="highlightB">The highlight b.</param>
1210+
/// <param name="shadowR">The shadow r.</param>
1211+
/// <param name="shadowG">The shadow g.</param>
1212+
/// <param name="shadowB">The shadow b.</param>
1213+
/// <returns>The generated raw texture buffer.</returns>
1214+
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
1215+
public static RawTextureBuffer GenerateStylizedFoliage(
1216+
int width,
1217+
int height,
1218+
object noiseGenInstance,
1219+
int clusterSize = 32,
1220+
bool transparentGaps = true,
1221+
byte baseR = 52, byte baseG = 125, byte baseB = 38, // Mid foliage green
1222+
byte highlightR = 108, byte highlightG = 175, byte highlightB = 45, // Sunlit tint
1223+
byte shadowR = 28, byte shadowG = 75, byte shadowB = 26) // Ambient crevice green
1224+
{
1225+
var buffer = new RawTextureBuffer(width, height);
1226+
var span = buffer.AsSpan();
1227+
dynamic noiseGen = noiseGenInstance;
1228+
1229+
var rand = new Random(1337);
1230+
var gridCols = (width / clusterSize) + 2;
1231+
var gridRows = (height / clusterSize) + 2;
1232+
var pointsX = new int[gridCols, gridRows];
1233+
var pointsY = new int[gridCols, gridRows];
1234+
1235+
for (var y = 0; y < gridRows; y++)
1236+
{
1237+
for (var x = 0; x < gridCols; x++)
1238+
{
1239+
pointsX[x, y] = (x * clusterSize) + rand.Next(2, clusterSize - 2);
1240+
pointsY[x, y] = (y * clusterSize) + rand.Next(2, clusterSize - 2);
1241+
}
1242+
}
1243+
1244+
var idx = 0;
1245+
var maxRadius = clusterSize * 0.75;
1246+
1247+
for (var y = 0; y < height; y++)
1248+
{
1249+
for (var x = 0; x < width; x++)
1250+
{
1251+
// 1. Organic domain-warp for jagged leaf edges instead of smooth spheres
1252+
var warpX = ((double)noiseGen.SmoothNoise(x / 12.0, y / 12.0) - 0.5) * 8.0;
1253+
var warpY = ((double)noiseGen.SmoothNoise((x + 64) / 12.0, (y + 64) / 12.0) - 0.5) * 8.0;
1254+
1255+
var px = x + warpX;
1256+
var py = y + warpY;
1257+
1258+
var cellX = (int)px / clusterSize;
1259+
var cellY = (int)py / clusterSize;
1260+
1261+
var d1 = double.MaxValue;
1262+
var d2 = double.MaxValue;
1263+
1264+
var startX = Math.Max(0, cellX - 1);
1265+
var endX = Math.Min(gridCols - 1, cellX + 1);
1266+
var startY = Math.Max(0, cellY - 1);
1267+
var endY = Math.Min(gridRows - 1, cellY + 1);
1268+
1269+
for (var cy = startY; cy <= endY; cy++)
1270+
{
1271+
for (var cx = startX; cx <= endX; cx++)
1272+
{
1273+
var dx = px - pointsX[cx, cy];
1274+
var dy = py - pointsY[cx, cy];
1275+
var dist = Math.Sqrt(dx * dx + dy * dy);
1276+
1277+
if (dist < d1)
1278+
{
1279+
d2 = d1;
1280+
d1 = dist;
1281+
}
1282+
else if (dist < d2)
1283+
{
1284+
d2 = dist;
1285+
}
1286+
}
1287+
}
1288+
1289+
// Clump core factor: flat plateau in the center, dropping sharply at edges
1290+
var edgeDistance = (d2 - d1) / (clusterSize * 0.45);
1291+
var leafCoverage = Math.Clamp(edgeDistance, 0.0, 1.0);
1292+
1293+
// Large macro noise for subtle tone variations across the canopy
1294+
var canopyGradient = (double)noiseGen.SmoothNoise(x / 48.0, y / 48.0);
1295+
1296+
if (transparentGaps && leafCoverage < 0.25)
1297+
{
1298+
span[idx++] = 0;
1299+
span[idx++] = 0;
1300+
span[idx++] = 0;
1301+
span[idx++] = 0; // Transparent cutout for low-poly tree card meshes
1302+
continue;
1303+
}
1304+
1305+
// Soft color blend (No harsh black crevices!)
1306+
byte r, g, b;
1307+
if (canopyGradient > 0.5)
1308+
{
1309+
var t = (canopyGradient - 0.5) * 2.0;
1310+
r = (byte)(baseR + (highlightR - baseR) * t);
1311+
g = (byte)(baseG + (highlightG - baseG) * t);
1312+
b = (byte)(baseB + (highlightB - baseB) * t);
1313+
}
1314+
else
1315+
{
1316+
var t = (0.5 - canopyGradient) * 2.0;
1317+
r = (byte)(baseR + (shadowR - baseR) * t);
1318+
g = (byte)(baseG + (shadowG - baseG) * t);
1319+
b = (byte)(baseB + (shadowB - baseB) * t);
1320+
}
1321+
1322+
span[idx++] = b;
1323+
span[idx++] = g;
1324+
span[idx++] = r;
1325+
span[idx++] = 255;
1326+
}
1327+
}
1328+
1329+
return buffer;
1330+
}
1331+
10541332
/// <summary>
10551333
/// Generates a rough, furrowed tree bark texture using anisotropic domain-warped noise.
10561334
/// </summary>

0 commit comments

Comments
 (0)