diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeParts.java b/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeParts.java
index 50f744f..4030249 100644
--- a/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeParts.java
+++ b/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeParts.java
@@ -25,13 +25,14 @@ import glm.vec._3.Vec3;
 import glm.vec._4.Vec4;
 import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram.VertexBuilder;
 import ru.windcorp.progressia.client.graphics.texture.Texture;
+import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram.WRPVertexBuilder;
 import ru.windcorp.progressia.common.world.rels.AbsFace;
 
 public class ShapeParts {
 
 	private ShapeParts() {
 	}
-
+	
 	public static ShapePart createRectangle(
 		ShapeRenderProgram program,
 		Texture texture,
@@ -41,25 +42,65 @@ public class ShapeParts {
 		Vec3 height,
 		boolean flip
 	) {
-		VertexBuilder builder = program.getVertexBuilder();
+		return createRectangle(program, texture, colorMultiplier, origin, width, height, flip, null);
+	}
 
-		builder.addVertex(
-			origin,
-			colorMultiplier,
-			new Vec2(0, 0)
-		).addVertex(
-			origin.add_(height),
-			colorMultiplier,
-			new Vec2(0, 1)
-		).addVertex(
-			origin.add_(width),
-			colorMultiplier,
-			new Vec2(1, 0)
-		).addVertex(
-			origin.add_(width).add(height),
-			colorMultiplier,
-			new Vec2(1, 1)
-		);
+	public static ShapePart createRectangle(
+		ShapeRenderProgram program,
+		Texture texture,
+		Vec4 colorMultiplier,
+		Vec3 origin,
+		Vec3 width,
+		Vec3 height,
+		boolean flip,
+		Vec3 forcedNormals
+	) {
+		VertexBuilder builder = program.getVertexBuilder();
+		
+		if (forcedNormals != null && builder instanceof WRPVertexBuilder) {
+			((WRPVertexBuilder) builder).addVertex(
+				origin,
+				colorMultiplier,
+				new Vec2(0, 0),
+				forcedNormals
+			);
+			((WRPVertexBuilder) builder).addVertex(
+				origin.add_(height),
+				colorMultiplier,
+				new Vec2(0, 1),
+				forcedNormals
+			);
+			((WRPVertexBuilder) builder).addVertex(
+				origin.add_(width),
+				colorMultiplier,
+				new Vec2(1, 0),
+				forcedNormals
+			);
+			((WRPVertexBuilder) builder).addVertex(
+				origin.add_(width).add(height),
+				colorMultiplier,
+				new Vec2(1, 1),
+				forcedNormals
+			);
+		} else {
+			builder.addVertex(
+				origin,
+				colorMultiplier,
+				new Vec2(0, 0)
+			).addVertex(
+				origin.add_(height),
+				colorMultiplier,
+				new Vec2(0, 1)
+			).addVertex(
+				origin.add_(width),
+				colorMultiplier,
+				new Vec2(1, 0)
+			).addVertex(
+				origin.add_(width).add(height),
+				colorMultiplier,
+				new Vec2(1, 1)
+			);
+		}
 
 		ShortBuffer buffer = flip ? ShortBuffer.wrap(
 			new short[] {
diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureLoader.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureLoader.java
index 18f2326..1ce3998 100644
--- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureLoader.java
+++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TextureLoader.java
@@ -28,6 +28,7 @@ import javax.imageio.ImageIO;
 
 import ru.windcorp.progressia.common.resource.Resource;
 import ru.windcorp.progressia.common.util.BinUtil;
+import ru.windcorp.progressia.common.util.crash.CrashReports;
 
 public class TextureLoader {
 
@@ -99,7 +100,12 @@ public class TextureLoader {
 		TextureSettings settings
 	)
 		throws IOException {
-		return loadPixels(resource.getInputStream(), settings);
+		
+		InputStream stream = resource.getInputStream();
+		if (stream == null) {
+			throw CrashReports.report(null, "Texture \"%s\" not found", resource.getName());
+		}
+		return loadPixels(stream, settings);
 	}
 
 	private TextureLoader() {
diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/WorldRenderProgram.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/WorldRenderProgram.java
index c005454..55ef3f8 100644
--- a/src/main/java/ru/windcorp/progressia/client/graphics/world/WorldRenderProgram.java
+++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/WorldRenderProgram.java
@@ -199,6 +199,10 @@ public class WorldRenderProgram extends ShapeRenderProgram {
 		int offset = vertices.position() + index * getBytesPerVertex() + (3 * Float.BYTES +
 			4 * Float.BYTES +
 			2 * Float.BYTES);
+		
+		if (!Float.isNaN(vertices.getFloat(offset + 0 * Float.BYTES))) {
+			return; // normals are forced
+		}
 
 		vertices.putFloat(offset + 0 * Float.BYTES, normal.x);
 		vertices.putFloat(offset + 1 * Float.BYTES, normal.y);
@@ -212,7 +216,7 @@ public class WorldRenderProgram extends ShapeRenderProgram {
 		return new WRPVertexBuilder();
 	}
 
-	private static class WRPVertexBuilder implements VertexBuilder {
+	public static class WRPVertexBuilder implements VertexBuilder {
 		// TODO Throw VertexBuilders out the window and rewrite completely.
 		// I want to _extend_ VBs, not re-implement them for children of SRP
 
@@ -220,11 +224,17 @@ public class WorldRenderProgram extends ShapeRenderProgram {
 			final Vec3 position;
 			final Vec4 colorMultiplier;
 			final Vec2 textureCoords;
-
+			final Vec3 normals;
+			
 			Vertex(Vec3 position, Vec4 colorMultiplier, Vec2 textureCoords) {
+				this(position, colorMultiplier, textureCoords, new Vec3(Float.NaN, Float.NaN, Float.NaN));
+			}
+
+			Vertex(Vec3 position, Vec4 colorMultiplier, Vec2 textureCoords, Vec3 normals) {
 				this.position = position;
 				this.colorMultiplier = colorMultiplier;
 				this.textureCoords = textureCoords;
+				this.normals = normals;
 			}
 		}
 
@@ -291,6 +301,24 @@ public class WorldRenderProgram extends ShapeRenderProgram {
 
 			return this;
 		}
+		
+		public VertexBuilder addVertex(
+			Vec3 position,
+			Vec4 colorMultiplier,
+			Vec2 textureCoords,
+			Vec3 normals
+		) {
+			vertices.add(
+				new Vertex(
+					new Vec3(position),
+					new Vec4(colorMultiplier),
+					new Vec2(textureCoords),
+					new Vec3(normals)
+				)
+			);
+
+			return this;
+		}
 
 		@Override
 		public ByteBuffer assemble() {
@@ -309,9 +337,9 @@ public class WorldRenderProgram extends ShapeRenderProgram {
 					.putFloat(v.colorMultiplier.w)
 					.putFloat(v.textureCoords.x)
 					.putFloat(v.textureCoords.y)
-					.putFloat(Float.NaN)
-					.putFloat(Float.NaN)
-					.putFloat(Float.NaN);
+					.putFloat(v.normals.x)
+					.putFloat(v.normals.y)
+					.putFloat(v.normals.z);
 			}
 
 			result.flip();
diff --git a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderCross.java b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderCross.java
index 01af8f6..f312ed2 100644
--- a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderCross.java
+++ b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderCross.java
@@ -114,7 +114,8 @@ public class TileRenderCross extends TileRender implements TileOptimizedCustom {
 						origin,
 						width,
 						height,
-						false
+						false,
+						new Vec3(0, 0, 1)
 					)
 				);
 				output.accept(
@@ -125,7 +126,8 @@ public class TileRenderCross extends TileRender implements TileOptimizedCustom {
 						origin,
 						width,
 						height,
-						true
+						true,
+						new Vec3(0, 0, 1)
 					)
 				);
 			}
diff --git a/src/main/java/ru/windcorp/progressia/test/Flowers.java b/src/main/java/ru/windcorp/progressia/test/Flowers.java
new file mode 100644
index 0000000..9eb45a8
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/test/Flowers.java
@@ -0,0 +1,147 @@
+/*
+ * Progressia
+ * Copyright (C)  2020-2021  Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ */
+package ru.windcorp.progressia.test;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import ru.windcorp.progressia.client.graphics.texture.Texture;
+import ru.windcorp.progressia.client.world.tile.TileRenderRegistry;
+import ru.windcorp.progressia.client.world.tile.TileRenderTransparentSurface;
+import ru.windcorp.progressia.common.world.tile.TileData;
+import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
+import ru.windcorp.progressia.server.world.tile.HangingTileLogic;
+import ru.windcorp.progressia.server.world.tile.TileLogicRegistry;
+
+public class Flowers {
+
+	private static final int HERB_MAX_COUNT = 3;
+	private static final int TINY_MAX_COUNT = 8;
+	private static final float TINY_SIZE = 0.5f;
+
+	public enum FlowerVariant {
+
+		FLAT("Flat"),
+		TINY("Tiny"),
+		HERB("Herb");
+
+		private final String name;
+
+		private FlowerVariant(String name) {
+			this.name = name;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+	}
+
+	public static class Flower {
+
+		private final String name;
+
+		/**
+		 * 1 disables spawning, 0 spawns everywhere
+		 */
+		private final float rarity;
+
+		private final FlowerVariant[] requestedVariants;
+		private final Map tiles = new HashMap<>();
+
+		public Flower(String name, float rarity, FlowerVariant... variants) {
+			Objects.requireNonNull(name, "name");
+			Objects.requireNonNull(variants, "variants");
+
+			this.name = name;
+			this.rarity = rarity;
+			this.requestedVariants = variants;
+		}
+
+		public String getName() {
+			return name;
+		}
+		
+		public float getRarity() {
+			return rarity;
+		}
+
+		public TileData getTile(FlowerVariant variant) {
+			return tiles.get(variant);
+		}
+
+		public Collection getTiles() {
+			return tiles.values();
+		}
+
+		private void register() {
+			for (FlowerVariant variant : requestedVariants) {
+
+				String fullName = "Flower" + name + variant.getName();
+				String id = "Test:" + fullName;
+
+				TileData tileData = new TileData(id);
+				tiles.put(variant, tileData);
+				TileDataRegistry.getInstance().register(tileData);
+
+				Texture texture = TileRenderRegistry.getTileTexture(fullName);
+
+				TileLogicRegistry logic = TileLogicRegistry.getInstance();
+				TileRenderRegistry render = TileRenderRegistry.getInstance();
+
+				switch (variant) {
+				case HERB:
+					logic.register(new HangingTileLogic(id));
+					render.register(new TileRenderHerb(id, texture, HERB_MAX_COUNT));
+					break;
+				case TINY:
+					logic.register(new HangingTileLogic(id));
+					render.register(new TileRenderTinyFlower(id, texture, TINY_MAX_COUNT, TINY_SIZE));
+					break;
+				case FLAT:
+				default:
+					logic.register(new TestTileLogicGrass(id));
+					render.register(new TileRenderTransparentSurface(id, texture));
+					break;
+				}
+
+			}
+		}
+
+	}
+
+	private final Map flowersByName = Collections.synchronizedMap(new HashMap<>());
+
+	public Flower create(String name, float rarity, FlowerVariant... variants) {
+		Flower flower = new Flower(name, rarity, variants);
+		flowersByName.put(name, flower);
+		return flower;
+	}
+
+	public void registerAllFlowers() {
+		getFlowers().forEach(Flower::register);
+	}
+
+	public Collection getFlowers() {
+		return flowersByName.values();
+	}
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java
index 5e9ea98..e027cef 100644
--- a/src/main/java/ru/windcorp/progressia/test/TestContent.java
+++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java
@@ -62,6 +62,7 @@ import ru.windcorp.progressia.server.world.context.ServerTileStackContext;
 import ru.windcorp.progressia.server.world.entity.*;
 import ru.windcorp.progressia.server.world.generation.planet.PlanetGravityModel;
 import ru.windcorp.progressia.server.world.tile.*;
+import ru.windcorp.progressia.test.Flowers.FlowerVariant;
 import ru.windcorp.progressia.test.Rocks.RockType;
 import ru.windcorp.progressia.test.gen.TestGravityModel;
 
@@ -75,6 +76,7 @@ public class TestContent {
 	public static final List PLACEABLE_TILES = new ArrayList<>();
 
 	public static final Rocks ROCKS = new Rocks();
+	public static final Flowers FLOWERS = new Flowers();
 
 	public static void registerContent() {
 		registerWorldContent();
@@ -183,15 +185,8 @@ public class TestContent {
 			);
 			register(new TestTileLogicGrass(id));
 		});
-
-		Arrays.asList(
-			"Yellow",
-			"White",
-			"Purple",
-			"Blue"
-		).forEach(color -> {
-			registerSimplestTransparentTile(color + "Flowers");
-		});
+		
+		registerFlowers();
 
 		registerSimplestTransparentTile("Stones");
 
@@ -216,21 +211,12 @@ public class TestContent {
 		registerSimplestOpaqueTile("TilesLarge");
 		registerSimplestOpaqueTile("TilesSmall");
 
-		registerHerb("LowGrass", 6);
-		registerHerb("MediumGrass", 6);
-		registerHerb("TallGrass", 6);
-
-		Arrays.asList(
-			"Dandelion",
-			"Lavander"
-		).forEach(variant -> {
-			String fullName = "Tiny" + variant + "Flowers";
-			String id = "Test:" + fullName;
-
-			register(new TileData(id));
-			register(new TileRenderTinyFlower(id, getTileTexture(fullName), 8, 0.5f));
-			register(new HangingTileLogic(id));
-		});
+		registerHerb("GrassMeadow0", 6);
+		registerHerb("GrassMeadow1", 6);
+		registerHerb("GrassMeadow2", 6);
+		registerHerb("GrassBluegrass0", 6);
+		registerHerb("GrassBluegrass1", 6);
+		registerHerb("GrassBluegrass2", 6);
 
 		registerHerb("Bush", 1);
 		registerHerb("Fern", 3);
@@ -240,6 +226,21 @@ public class TestContent {
 		PLACEABLE_TILES.sort(Comparator.comparing(TileData::getId));
 	}
 
+	private static void registerFlowers() {
+		
+		final float common = 0.8f;
+		
+		FLOWERS.create("Clover", common, FlowerVariant.HERB, FlowerVariant.TINY, FlowerVariant.FLAT);
+		FLOWERS.create("Dandelion", common, FlowerVariant.TINY, FlowerVariant.FLAT);
+		FLOWERS.create("Geranium", common, FlowerVariant.TINY, FlowerVariant.FLAT);
+		FLOWERS.create("Knapweed", common, FlowerVariant.TINY, FlowerVariant.FLAT);
+		FLOWERS.create("YellowPea", common, FlowerVariant.TINY, FlowerVariant.FLAT);
+		FLOWERS.create("Daisy", common, FlowerVariant.TINY, FlowerVariant.FLAT);
+		FLOWERS.create("Lavander", common, FlowerVariant.TINY, FlowerVariant.FLAT);
+		
+		FLOWERS.registerAllFlowers();
+	}
+
 	private static void registerSimplestBlock(String name) {
 		String id = "Test:" + name;
 		register(new BlockData(id));
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/Fields.java b/src/main/java/ru/windcorp/progressia/test/gen/Fields.java
index 26ccdca..36abefb 100644
--- a/src/main/java/ru/windcorp/progressia/test/gen/Fields.java
+++ b/src/main/java/ru/windcorp/progressia/test/gen/Fields.java
@@ -218,6 +218,10 @@ public class Fields {
 	public static Field anti(Field f) {
 		return tweak(f, 1, -1, 1);
 	}
+	
+	public static Field rarify(Field f, float rarity) {
+		return (x, y) -> Math.max((f.compute(x, y) - rarity) / (1 - rarity), 0);
+	}
 
 	public static Field octaves(Field f, double scaleFactor, double amplitudeFactor, int octaves) {
 		return (x, y) -> {
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/TestGenerationConfig.java b/src/main/java/ru/windcorp/progressia/test/gen/TestGenerationConfig.java
index 1fe8334..1896db4 100644
--- a/src/main/java/ru/windcorp/progressia/test/gen/TestGenerationConfig.java
+++ b/src/main/java/ru/windcorp/progressia/test/gen/TestGenerationConfig.java
@@ -26,6 +26,7 @@ import java.util.function.Function;
 import ru.windcorp.progressia.common.Units;
 import ru.windcorp.progressia.common.util.noise.discrete.WorleyProceduralNoise;
 import ru.windcorp.progressia.common.world.Coordinates;
+import ru.windcorp.progressia.common.world.rels.AbsFace;
 import ru.windcorp.progressia.server.Server;
 import ru.windcorp.progressia.server.world.generation.WorldGenerator;
 import ru.windcorp.progressia.server.world.generation.planet.Planet;
@@ -125,19 +126,16 @@ public class TestGenerationConfig {
 			)
 		);
 
-		Function floweriness = flowerName -> fields.register(
-			"Test:Flower" + flowerName,
-			f -> multiply(
-				selectPositive(squash(scale(octaves(fields.primitive(), 2, 3), 100), 2), 1, 0.5),
-				tweak(fields.get("Test:Forest", f), 1, -1, 1.1),
-				anti(squash(fields.get("Test:Cliff", f), 10))
-			)
+		Function floweriness = f -> multiply(
+			amplify(withMin(squash(scale(octaves(fields.primitive(), 2, 3), 100), 5), 0), 2),
+			tweak(fields.get("Test:Forest", f), 1, -1, 1.1),
+			anti(squash(fields.get("Test:Cliff", f), 10))
 		);
 
 		features.add(new TestBushFeature("Test:BushFeature", forestiness));
 		features.add(new TestTreeFeature("Test:TreeFeature", forestiness));
 		features.add(new TestGrassFeature("Test:GrassFeature", grassiness));
-		features.add(new TestFlowerFeature("Test:FlowerFeature", floweriness));
+		features.add(new TestFlowerFeature("Test:FlowerFeature", TestContent.FLOWERS, floweriness, fields));
 	}
 
 }
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/feature/TestFlowerFeature.java b/src/main/java/ru/windcorp/progressia/test/gen/feature/TestFlowerFeature.java
index 19a97aa..03fd251 100644
--- a/src/main/java/ru/windcorp/progressia/test/gen/feature/TestFlowerFeature.java
+++ b/src/main/java/ru/windcorp/progressia/test/gen/feature/TestFlowerFeature.java
@@ -17,35 +17,73 @@
  */
 package ru.windcorp.progressia.test.gen.feature;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 import java.util.function.Function;
 
 import com.google.common.collect.ImmutableSet;
 
+import ru.windcorp.progressia.common.world.rels.AbsFace;
 import ru.windcorp.progressia.common.world.rels.RelFace;
 import ru.windcorp.progressia.common.world.tile.TileData;
-import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
 import ru.windcorp.progressia.server.world.generation.surface.SurfaceFloatField;
 import ru.windcorp.progressia.server.world.generation.surface.SurfaceTopLayerFeature;
 import ru.windcorp.progressia.server.world.generation.surface.context.SurfaceBlockContext;
+import ru.windcorp.progressia.test.Flowers;
+import ru.windcorp.progressia.test.Flowers.Flower;
+import ru.windcorp.progressia.test.Flowers.FlowerVariant;
 import ru.windcorp.progressia.test.TestContent;
+import ru.windcorp.progressia.test.gen.Fields;
+import ru.windcorp.progressia.test.gen.Fields.Field;
 
 public class TestFlowerFeature extends SurfaceTopLayerFeature {
 
 	private static class FlowerGenerator {
-		private final TileData tile;
+		
+		private final TileData[] variants;
 		private final SurfaceFloatField floweriness;
 
-		public FlowerGenerator(TileData tile, Function flowerinessGenerator) {
-			this.tile = tile;
-			this.floweriness = flowerinessGenerator.apply(tile.getName());
+		public FlowerGenerator(Flower flower, Function flowerinessGenerator, Fields fields) {
+			this.floweriness = fields.register(
+				"Test:Flower" + flower.getName(),
+				f -> Fields.rarify(flowerinessGenerator.apply(f), flower.getRarity())
+			);
+			
+			List tiles = new ArrayList<>();
+			for (FlowerVariant variant : FlowerVariant.values()) {
+				TileData tile = flower.getTile(variant);
+				if (tile == null) {
+					continue;
+				}
+				
+				tiles.add(tile);
+			}
+			
+			this.variants = tiles.toArray(new TileData[tiles.size()]);
 		}
 
 		public void generate(SurfaceBlockContext context) {
-			if (context.getRandom().nextDouble() < floweriness.get(context)) {
-				context.addTile(RelFace.UP, tile);
+			float floweriness = this.floweriness.get(context);
+			
+			if (floweriness <= 0) {
+				return;
 			}
+			
+			float random = context.getRandom().nextFloat();
+			
+			int variant = (int) Math.floor((random + floweriness - 1) * variants.length);
+			
+			if (variant < 0) {
+				return;
+			} else if (variant >= variants.length) {
+				// Screw doing float math properly, just clamp it
+				variant = variants.length - 1;
+			}
+			
+			context.addTile(RelFace.UP, variants[variant]);
 		}
+		
 	}
 
 	private final Set soilWhitelist;
@@ -58,12 +96,11 @@ public class TestFlowerFeature extends SurfaceTopLayerFeature {
 
 	private final FlowerGenerator[] flowers;
 
-	public TestFlowerFeature(String id, Function flowerinessGenerator) {
+	public TestFlowerFeature(String id, Flowers flowers, Function flowerinessGenerator, Fields fields) {
 		super(id);
 
-		this.flowers = TileDataRegistry.getInstance().values().stream()
-			.filter(tile -> tile.getName().endsWith("Flowers"))
-			.map(tile -> new FlowerGenerator(tile, flowerinessGenerator))
+		this.flowers = flowers.getFlowers().stream()
+			.map(flower -> new FlowerGenerator(flower, flowerinessGenerator, fields))
 			.toArray(FlowerGenerator[]::new);
 	}
 
diff --git a/src/main/java/ru/windcorp/progressia/test/gen/feature/TestGrassFeature.java b/src/main/java/ru/windcorp/progressia/test/gen/feature/TestGrassFeature.java
index d854bf4..d7b4bae 100644
--- a/src/main/java/ru/windcorp/progressia/test/gen/feature/TestGrassFeature.java
+++ b/src/main/java/ru/windcorp/progressia/test/gen/feature/TestGrassFeature.java
@@ -19,6 +19,7 @@ package ru.windcorp.progressia.test.gen.feature;
 
 import java.util.List;
 import java.util.Set;
+import java.util.function.Function;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -57,12 +58,26 @@ public class TestGrassFeature extends SurfaceTopLayerFeature {
 		flatGrasses.put(0.1f, 0.2f, TileDataRegistry.getInstance().get("Test:GrassWeb"));
 		flatGrasses.put(0.05f, 0.1f, TileDataRegistry.getInstance().get("Test:GrassThreads"));
 	}
+	
+	private static final Function randomPicker(String commonId, String rareId) {
+		TileData common = TileDataRegistry.getInstance().get(commonId);
+		TileData rare = TileDataRegistry.getInstance().get(rareId);
+		final float rareChance = 0.2f;
+		
+		return context -> {
+			if (context.getRandom().nextFloat() < rareChance) {
+				return rare;
+			} else {
+				return common;
+			}
+		};
+	}
 
-	private final FloatRangeMap herbGrasses = new ArrayFloatRangeMap<>();
+	private final FloatRangeMap> herbGrasses = new ArrayFloatRangeMap<>();
 	{
-		herbGrasses.put(0.6f, 1, TileDataRegistry.getInstance().get("Test:TallGrass"));
-		herbGrasses.put(0.4f, 0.6f, TileDataRegistry.getInstance().get("Test:MediumGrass"));
-		herbGrasses.put(0.1f, 0.4f, TileDataRegistry.getInstance().get("Test:LowGrass"));
+		herbGrasses.put(0.6f, 1, randomPicker("Test:GrassMeadow2", "Test:GrassBluegrass2"));
+		herbGrasses.put(0.4f, 0.6f, randomPicker("Test:GrassMeadow1", "Test:GrassBluegrass1"));
+		herbGrasses.put(0.1f, 0.4f, randomPicker("Test:GrassMeadow0", "Test:GrassBluegrass0"));
 	}
 
 	private final List scatter = ImmutableList.of(
@@ -134,9 +149,9 @@ public class TestGrassFeature extends SurfaceTopLayerFeature {
 		}
 
 		if (context.getRandom().nextDouble() < grassiness) {
-			TileData herbGrass = herbGrasses.get((float) grassiness);
+			Function herbGrass = herbGrasses.get((float) grassiness);
 			if (herbGrass != null) {
-				context.addTile(RelFace.UP, herbGrass);
+				context.addTile(RelFace.UP, herbGrass.apply(context));
 			}
 		}
 	}
diff --git a/src/main/resources/assets/textures/tiles/BlueFlowers.png b/src/main/resources/assets/textures/tiles/BlueFlowers.png
deleted file mode 100644
index 82c5047..0000000
Binary files a/src/main/resources/assets/textures/tiles/BlueFlowers.png and /dev/null differ
diff --git a/src/main/resources/assets/textures/tiles/Bush.png b/src/main/resources/assets/textures/tiles/Bush.png
index 0922df3..2716318 100644
Binary files a/src/main/resources/assets/textures/tiles/Bush.png and b/src/main/resources/assets/textures/tiles/Bush.png differ
diff --git a/src/main/resources/assets/textures/tiles/Fern.png b/src/main/resources/assets/textures/tiles/Fern.png
index bf5e48c..f1bcf5f 100644
Binary files a/src/main/resources/assets/textures/tiles/Fern.png and b/src/main/resources/assets/textures/tiles/Fern.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerCloverFlat.png b/src/main/resources/assets/textures/tiles/FlowerCloverFlat.png
new file mode 100644
index 0000000..c8d54c7
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerCloverFlat.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerCloverHerb.png b/src/main/resources/assets/textures/tiles/FlowerCloverHerb.png
new file mode 100644
index 0000000..2d6c505
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerCloverHerb.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerCloverTiny.png b/src/main/resources/assets/textures/tiles/FlowerCloverTiny.png
new file mode 100644
index 0000000..31a0498
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerCloverTiny.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerDaisyFlat.png b/src/main/resources/assets/textures/tiles/FlowerDaisyFlat.png
new file mode 100644
index 0000000..b054328
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerDaisyFlat.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerDaisyTiny.png b/src/main/resources/assets/textures/tiles/FlowerDaisyTiny.png
new file mode 100644
index 0000000..5fca23e
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerDaisyTiny.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerDandelionFlat.png b/src/main/resources/assets/textures/tiles/FlowerDandelionFlat.png
new file mode 100644
index 0000000..c72e70d
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerDandelionFlat.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerDandelionTiny.png b/src/main/resources/assets/textures/tiles/FlowerDandelionTiny.png
new file mode 100644
index 0000000..3477cd4
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerDandelionTiny.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerGeraniumFlat.png b/src/main/resources/assets/textures/tiles/FlowerGeraniumFlat.png
new file mode 100644
index 0000000..16ff34c
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerGeraniumFlat.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerGeraniumTiny.png b/src/main/resources/assets/textures/tiles/FlowerGeraniumTiny.png
new file mode 100644
index 0000000..5aa2521
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerGeraniumTiny.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerKnapweedFlat.png b/src/main/resources/assets/textures/tiles/FlowerKnapweedFlat.png
new file mode 100644
index 0000000..dd888ea
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerKnapweedFlat.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerKnapweedTiny.png b/src/main/resources/assets/textures/tiles/FlowerKnapweedTiny.png
new file mode 100644
index 0000000..226d59e
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerKnapweedTiny.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerLavanderFlat.png b/src/main/resources/assets/textures/tiles/FlowerLavanderFlat.png
new file mode 100644
index 0000000..52be478
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerLavanderFlat.png differ
diff --git a/src/main/resources/assets/textures/tiles/TinyLavanderFlowers.png b/src/main/resources/assets/textures/tiles/FlowerLavanderTiny.png
similarity index 100%
rename from src/main/resources/assets/textures/tiles/TinyLavanderFlowers.png
rename to src/main/resources/assets/textures/tiles/FlowerLavanderTiny.png
diff --git a/src/main/resources/assets/textures/tiles/FlowerYellowPeaFlat.png b/src/main/resources/assets/textures/tiles/FlowerYellowPeaFlat.png
new file mode 100644
index 0000000..e254421
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerYellowPeaFlat.png differ
diff --git a/src/main/resources/assets/textures/tiles/FlowerYellowPeaTiny.png b/src/main/resources/assets/textures/tiles/FlowerYellowPeaTiny.png
new file mode 100644
index 0000000..b8aa6b3
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/FlowerYellowPeaTiny.png differ
diff --git a/src/main/resources/assets/textures/tiles/GrassBluegrass0.png b/src/main/resources/assets/textures/tiles/GrassBluegrass0.png
new file mode 100644
index 0000000..6d61e41
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/GrassBluegrass0.png differ
diff --git a/src/main/resources/assets/textures/tiles/GrassBluegrass1.png b/src/main/resources/assets/textures/tiles/GrassBluegrass1.png
new file mode 100644
index 0000000..6ae11d0
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/GrassBluegrass1.png differ
diff --git a/src/main/resources/assets/textures/tiles/GrassBluegrass2.png b/src/main/resources/assets/textures/tiles/GrassBluegrass2.png
new file mode 100644
index 0000000..1370fb3
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/GrassBluegrass2.png differ
diff --git a/src/main/resources/assets/textures/tiles/GrassMeadow0.png b/src/main/resources/assets/textures/tiles/GrassMeadow0.png
new file mode 100644
index 0000000..221fb88
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/GrassMeadow0.png differ
diff --git a/src/main/resources/assets/textures/tiles/GrassMeadow1.png b/src/main/resources/assets/textures/tiles/GrassMeadow1.png
new file mode 100644
index 0000000..7aff0be
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/GrassMeadow1.png differ
diff --git a/src/main/resources/assets/textures/tiles/GrassMeadow2.png b/src/main/resources/assets/textures/tiles/GrassMeadow2.png
new file mode 100644
index 0000000..abf1632
Binary files /dev/null and b/src/main/resources/assets/textures/tiles/GrassMeadow2.png differ
diff --git a/src/main/resources/assets/textures/tiles/LowGrass.png b/src/main/resources/assets/textures/tiles/LowGrass.png
deleted file mode 100644
index 2f3726f..0000000
Binary files a/src/main/resources/assets/textures/tiles/LowGrass.png and /dev/null differ
diff --git a/src/main/resources/assets/textures/tiles/MediumGrass.png b/src/main/resources/assets/textures/tiles/MediumGrass.png
deleted file mode 100644
index 7115d2f..0000000
Binary files a/src/main/resources/assets/textures/tiles/MediumGrass.png and /dev/null differ
diff --git a/src/main/resources/assets/textures/tiles/PurpleFlowers.png b/src/main/resources/assets/textures/tiles/PurpleFlowers.png
deleted file mode 100644
index e118fdd..0000000
Binary files a/src/main/resources/assets/textures/tiles/PurpleFlowers.png and /dev/null differ
diff --git a/src/main/resources/assets/textures/tiles/TallGrass.png b/src/main/resources/assets/textures/tiles/TallGrass.png
deleted file mode 100644
index ce41bcf..0000000
Binary files a/src/main/resources/assets/textures/tiles/TallGrass.png and /dev/null differ
diff --git a/src/main/resources/assets/textures/tiles/TinyDandelionFlowers.png b/src/main/resources/assets/textures/tiles/TinyDandelionFlowers.png
deleted file mode 100644
index 35f651c..0000000
Binary files a/src/main/resources/assets/textures/tiles/TinyDandelionFlowers.png and /dev/null differ
diff --git a/src/main/resources/assets/textures/tiles/WhiteFlowers.png b/src/main/resources/assets/textures/tiles/WhiteFlowers.png
deleted file mode 100644
index 84a987a..0000000
Binary files a/src/main/resources/assets/textures/tiles/WhiteFlowers.png and /dev/null differ
diff --git a/src/main/resources/assets/textures/tiles/YellowFlowers.png b/src/main/resources/assets/textures/tiles/YellowFlowers.png
deleted file mode 100644
index 3f7b8bc..0000000
Binary files a/src/main/resources/assets/textures/tiles/YellowFlowers.png and /dev/null differ