浏览代码

Update Terrain :
remove Tile preview, replaced by a decal
fix undo delete
add castShadow as an option

ShiroSmith 6 年之前
父节点
当前提交
41b50c8235

+ 38 - 74
hide/prefab/terrain/Brush.hx

@@ -1,4 +1,6 @@
 package hide.prefab.terrain;
+import h3d.mat.Stencil;
+import hrt.prefab.l3d.AdvancedDecal;
 using Lambda;
 import hxd.Key as K;
 
@@ -63,89 +65,51 @@ class BrushMode {
 	public function new(){}
 }
 
-class BrushPreview {
+class BrushPreview extends h3d.scene.Object {
 
 	var terrain : hrt.prefab.terrain.TerrainMesh;
-	var tiles : Array<TilePreviewMesh> = [];
-	var grid : h3d.prim.Grid;
+	var mesh : h3d.scene.pbr.Decal;
+	var shader : h3d.shader.pbr.VolumeDecal.DecalOverlay;
 
-	public function new(terrain){
+	public function new( terrain : hrt.prefab.terrain.TerrainMesh ) {
+		super(terrain.getScene());
 		this.terrain = terrain;
-		grid = new h3d.prim.Grid( terrain.cellCount, terrain.cellCount, terrain.cellSize, terrain.cellSize);
-		grid.addUVs();
-		grid.addNormals();
+		mesh = new h3d.scene.pbr.Decal(h3d.prim.Cube.defaultUnitCube(), this);
+		shader = new h3d.shader.pbr.VolumeDecal.DecalOverlay();
+		mesh.material.mainPass.addShader(shader);
+		mesh.material.mainPass.setPassName("afterTonemappingDecal");
+		mesh.material.mainPass.depthWrite = false;
+		mesh.material.mainPass.depthTest = GreaterEqual;
+		mesh.material.mainPass.culling = Front;
+		mesh.material.shadows = false;
+		mesh.material.blendMode = Alpha;
+		mesh.scaleZ = 1000;
+		shader.fadeStart = 1;
+		shader.fadeEnd = 0;
+		shader.fadePower = 1;
+		shader.emissive = 0;
+		shader.CENTERED = true;
+
+		// Only draw the preview on terrain
+		mesh.material.mainPass.stencil = new h3d.mat.Stencil();
+		mesh.material.mainPass.stencil.setFunc(Equal, 0x01, 0x01, 0x01);
+		mesh.material.mainPass.stencil.setOp(Keep, Keep, Keep);
 	}
 
-	public function dispose(){
-		for(tile in tiles)
-			tile.remove();
+	public function setBrushTexture( texture : h3d.mat.Texture ) {
+		shader.colorTexture = texture;
 	}
 
-	public function addPreviewMeshAt(x : Int, y : Int, brush : Brush, brushPos : h3d.Vector, ctx : hrt.prefab.Context) : TilePreviewMesh {
-		var camera = @:privateAccess ctx.local3d.getScene().camera;
-		var dir = camera.pos.sub(new h3d.Vector(terrain.getAbsPos().tx, terrain.getAbsPos().ty, terrain.getAbsPos().tz));
-		var offsetDir = dir.z < 0 ? -1: 1;
-		var tilePreview = null;
-		for(tile in tiles){
-			if(tile.used) continue;
-			tilePreview = tile;
-		}
-		if(tilePreview == null){
-			tilePreview = new TilePreviewMesh(grid, terrain);
-			tiles.push(tilePreview);
-		}
-		tilePreview.used = true;
-		var t = terrain.getTile(x,y);
-		tilePreview.heightMap = t == null ? null : t.heightMap;
-		tilePreview.shader.heightMapSize = terrain.heightMapResolution;
-		var pos = new h3d.Vector(x * terrain.tileSize, y * terrain.tileSize);
-		tilePreview.setPosition(pos.x, pos.y, pos.z + 0.1 * terrain.scaleZ * offsetDir);
-		tilePreview.visible = true;
-		tilePreview.shader.brushTex = brush.tex;
-		tilePreview.shader.brushSize =  brush.size;
-		tilePreview.shader.brushPos = brushPos;
-		return tilePreview;
+	public function previewAt( brush : Brush, pos : h3d.Vector ) {
+		setPosition(pos.x, pos.y, pos.z);
+		setBrushTexture( brush.tex );
+		setScale(brush.size);
+		visible = true;
 	}
 
-	public function reset(){
-		for(tile in tiles){
-			tile.used = false;
-			tile.visible = false;
-		}
-	}
-
-	public function refreshMesh(){
-		if(grid != null) grid.dispose();
-		grid = new h3d.prim.Grid( terrain.cellCount, terrain.cellCount, terrain.cellSize, terrain.cellSize);
-		grid.addUVs();
-		grid.addNormals();
-		for(tile in tiles)
-			tile.primitive = grid;
-	}
-}
-
-class TilePreviewMesh extends h3d.scene.Mesh {
-	public var used = false;
-	public var heightMap : h3d.mat.Texture;
-	public var shader : hide.prefab.terrain.TilePreview;
-
-	public function new(prim, parent){
-		super(prim, null, parent);
-		material.setDefaultProps("ui");
-		material.shadows = false;
-		material.blendMode = AlphaAdd;
-		shader = new hide.prefab.terrain.TilePreview();
-		material.mainPass.addShader(shader);
-	}
-
-	override function emit( ctx : h3d.scene.RenderContext ) {
-		if(heightMap == null) return;
-		super.emit(ctx);
-	}
-
-	override function sync(ctx : h3d.scene.RenderContext) {
-		shader.heightMap = heightMap;
-		shader.heightMapSize = heightMap.width;
-		shader.primSize = Std.downcast(parent, hrt.prefab.terrain.TerrainMesh).tileSize;
+	public function reset() {
+		setPosition(0,0,0);
+		visible = false;
+		shader.colorTexture = null;
 	}
 }

+ 40 - 45
hide/prefab/terrain/TerrainEditor.hx

@@ -35,63 +35,65 @@ class TileRevertData {
 
 @:access(hrt.prefab.terrain.TerrainMesh)
 @:access(hrt.prefab.terrain.Terrain)
+@:access(hrt.prefab.terrain.Tile)
 class TerrainEditor {
 
-	public var currentBrush : Brush;
+	public var currentBrush = new Brush();
 	public var currentSurface : hrt.prefab.terrain.Surface;
 	public var textureType = ["_Albedo", "_Normal", "_MetallicGlossAO"];
 	public var autoCreateTile = false;
 	public var editContext : hide.prefab.EditContext;
+
+	// Debug
+	var renderMode : RenderMode = PBR;
+	// Edition
 	var brushPreview : hide.prefab.terrain.Brush.BrushPreview;
 	var interactive : h2d.Interactive;
 	var remainingDist = 0.0;
 	var lastPos : h3d.Vector;
-	var copyPass : h3d.pass.Copy;
 	var heightStrokeBufferArray : hide.prefab.terrain.StrokeBuffer.StrokeBufferArray;
 	var weightStrokeBufferArray : hide.prefab.terrain.StrokeBuffer.StrokeBufferArray;
-
+	// Shader for edition
+	var copyPass = new h3d.pass.Copy();
 	var normalizeWeight = new h3d.pass.ScreenFx(new hide.prefab.terrain.NormalizeWeight());
 	var clampWeight = new h3d.pass.ScreenFx(new hide.prefab.terrain.ClampWeight());
 	var generateIndex = new h3d.pass.ScreenFx(new hide.prefab.terrain.GenerateIndex());
 	var swapIndex = new h3d.pass.ScreenFx(new hide.prefab.terrain.SwapIndex());
 	var setHeight = new h3d.pass.ScreenFx(new hide.prefab.terrain.SetHeight());
 	var smoothHeight = new h3d.pass.ScreenFx(new hide.prefab.terrain.SmoothHeight());
-
+	// Revert 
 	var terrainPrefab : hrt.prefab.terrain.Terrain;
 	var undo : hide.ui.UndoHistory;
-	var tileTrashBin : Array<hrt.prefab.terrain.Tile> = [];
+	var tileTrashBin : Array<TileRevertData> = [];
 	var paintRevertDatas : Array<TileRevertData> = [];
+	// Render UV offscreen
 	var uvTexPixels : hxd.Pixels.PixelsFloat;
 	var uvTex : h3d.mat.Texture;
 	var uvTexRes = 0.5;
-	var customScene : h3d.scene.Scene;
-	var customRenderer : hide.prefab.terrain.CustomRenderer;
-	var renderMode : RenderMode = PBR;
+	var customScene = new h3d.scene.Scene(false, false);
+	var customRenderer = new hide.prefab.terrain.CustomRenderer("terrainUV");
 
 	public function new( terrainPrefab : hrt.prefab.terrain.Terrain, undo : hide.ui.UndoHistory ) {
 		this.terrainPrefab = terrainPrefab;
 		this.undo = undo;
+		renderMode = terrainPrefab.showChecker ? Checker : PBR;
 		autoCreateTile = terrainPrefab.autoCreateTile;
+
 		brushPreview = new hide.prefab.terrain.Brush.BrushPreview(terrainPrefab.terrain);
-		brushPreview.refreshMesh();
-		currentBrush = new Brush();
-		copyPass = new h3d.pass.Copy();
+
 		heightStrokeBufferArray = new hide.prefab.terrain.StrokeBuffer.StrokeBufferArray(RGBA32F, terrainPrefab.heightMapResolution + 1);
 		weightStrokeBufferArray = new hide.prefab.terrain.StrokeBuffer.StrokeBufferArray(R8, terrainPrefab.weightMapResolution);
-		customRenderer = new hide.prefab.terrain.CustomRenderer("terrainUV");
-		customScene = new h3d.scene.Scene(false, false);
 		customScene.renderer = customRenderer;
 		#if debug
 		customScene.checkPasses = false;
 		#end
-		renderMode = terrainPrefab.showChecker ? Checker : PBR;
 	}
 
 	public function dispose() {
 		if(uvTex != null) uvTex.dispose();
 		heightStrokeBufferArray.dispose();
 		weightStrokeBufferArray.dispose();
-		brushPreview.dispose();
+		brushPreview.remove();
 	}
 
 	public function saveTextures()  {
@@ -192,7 +194,6 @@ class TerrainEditor {
 	}
 
 	public function refresh() {
-		brushPreview.refreshMesh();
 		heightStrokeBufferArray.refresh(terrainPrefab.heightMapResolution + 1);
 		weightStrokeBufferArray.refresh(terrainPrefab.weightMapResolution);
 	}
@@ -278,7 +279,22 @@ class TerrainEditor {
 			tileTrashBin = [];
 			undo.change(Custom(function(undo) {
 				for( t in tileTrashBinTmp ) {
-					undo ? terrainPrefab.terrain.addTile(t, true) : terrainPrefab.terrain.removeTile(t);
+					if( undo ) {
+						var tile = terrainPrefab.terrain.createTile(t.x, t.y);
+						tile.material.mainPass.stencil = new h3d.mat.Stencil();
+						tile.material.mainPass.stencil.setFunc(Always, 0x01, 0x01, 0x01);
+						tile.material.mainPass.stencil.setOp(Keep, Keep, Replace);
+						tile.refreshHeightMap();
+						tile.refreshIndexMap();
+						tile.refreshSurfaceWeights();
+						tile.heightMap.uploadPixels(t.prevHeightMapPixels);
+						tile.surfaceIndexMap.uploadPixels(t.prevSurfaceIndexMapPixels);
+						for( i in 0 ... t.prevWeightMapPixels.length )
+							tile.surfaceWeights[i].uploadPixels(t.prevWeightMapPixels[i]);
+						tile.generateWeightArray();
+					}
+					else
+						terrainPrefab.terrain.removeTileAt(t.x, t.y);
 				}
 			}));
 			tileTrashBin = [];
@@ -518,7 +534,6 @@ class TerrainEditor {
 
 	function worldToScreen( wx: Float, wy: Float, wz: Float, ctx : Context ) {
 		if( ctx == null || ctx.local3d == null || ctx.local3d.getScene() == null || ctx.local2d == null || ctx.local2d.getScene() == null ) return new h2d.col.Point();
-		var s2d = @:privateAccess ctx.local2d.getScene();
 		var camera = @:privateAccess ctx.local3d.getScene().camera;
 		var pt = camera.project(wx, wy, wz, h3d.Engine.getCurrent().width, h3d.Engine.getCurrent().height);
 		return new h2d.col.Point(hxd.Math.abs(pt.x), hxd.Math.abs(pt.y));
@@ -556,14 +571,7 @@ class TerrainEditor {
 		brushPreview.reset();
 		if( currentBrush.brushMode.mode == Delete || currentBrush.bitmap == null ) return;
 		var brushWorldPos = uvTexPixels == null ? worldPos : getBrushWorldPosFromTex(worldPos, ctx);
-		if( brushWorldPos == null ) return;
-		var tiles = terrainPrefab.terrain.getTiles(brushWorldPos.x, brushWorldPos.y, currentBrush.size / 2.0 , false);
-		for( i in 0 ... tiles.length) {
-			var tile = tiles[i];
-			var brushPos = tile.globalToLocal(brushWorldPos.clone());
-			brushPos.scale3(1.0 / terrainPrefab.tileSize);
-			brushPreview.addPreviewMeshAt(tile.tileX, tile.tileY, currentBrush, brushPos, ctx);
-		}
+		brushPreview.previewAt(currentBrush, brushWorldPos);
 	}
 
 	function applyBrush( pos : h3d.Vector, ctx : Context ) {
@@ -622,8 +630,13 @@ class TerrainEditor {
 		if( brushWorldPos == null ) return;
 		var tile = terrainPrefab.terrain.getTileAtWorldPos(brushWorldPos.x, brushWorldPos.y);
 		if( tile == null ) return;
+		var trd = new TileRevertData(tile.tileX, tile.tileY);
+		trd.prevHeightMapPixels = tile.heightMap.capturePixels();
+		trd.prevSurfaceIndexMapPixels = tile.surfaceIndexMap.capturePixels();
+		for( w in tile.surfaceWeights )
+			trd.prevWeightMapPixels.push(w.capturePixels());
+		tileTrashBin.push(trd);
 		terrainPrefab.terrain.removeTile(tile);
-		tileTrashBin.push(tile);
 		renderTerrainUV(ctx);
 	}
 
@@ -1031,24 +1044,6 @@ class TerrainEditor {
 		});
 		refreshBrushMode(props, ctx);
 
-		// Save Button
-		/*props.append(
-			'<div align="center">
-				<input type="button" value="Save" class="save" />
-			</div>');
-		props.find(".save").click(function(_) {
-			var datPath = new haxe.io.Path(ctx.rootContext.shared.currentPath);
-			datPath.ext = "dat";
-			var fullPath = ctx.ide.getPath(datPath.toString() + "/" + terrainPrefab.name);
-			if( sys.FileSystem.isDirectory(fullPath) ) {
-				var files = sys.FileSystem.readDirectory(fullPath);
-				for( f in files )
-					sys.FileSystem.deleteFile(fullPath + "/" + f);
-			}
-			terrainPrefab.saveWeightTextures(ctx.rootContext);
-			terrainPrefab.saveHeightTextures(ctx.rootContext);
-		});*/
-
 		var brushes : Array<Dynamic> = ctx.scene.config.get("terrain.brushes");
 		var brushesContainer = props.find(".terrain-brushes");
 		function refreshBrushes() {

+ 0 - 34
hide/prefab/terrain/TilePreview.hx

@@ -1,34 +0,0 @@
-package hide.prefab.terrain;
-
-class TilePreview extends hxsl.Shader {
-
-	static var SRC = {
-
-		@:import h3d.shader.BaseMesh;
-		@param var heightMap : Sampler2D;
-		@param var heightMapSize : Float;
-		@param var primSize : Float;
-
-		@param var brushTex : Sampler2D;
-		@param var brushSize : Float;
-		@param var brushPos : Vec2;
-
-		function vertex() {
-			var calculatedUV = input.position.xy / primSize;
-			var terrainUV = (calculatedUV * (heightMapSize - 1)) / heightMapSize;
-			terrainUV += 0.5 / heightMapSize;
-			transformedPosition += (vec3(0,0, heightMap.get(terrainUV).r) * global.modelView.mat3());
-		}
-
-		function fragment() {
-			var tilePos = (input.position.xy / primSize);
-			var brushUV = tilePos - (brushPos - (brushSize / (2.0 * primSize)));
-			brushUV /= (brushSize / primSize);
-			pixelColor = vec4(brushTex.get(brushUV).r * vec3(1,1,1), min(0.7,brushTex.get(brushUV).r));
-
-			if(brushUV.x < 0 || brushUV.x > 1 || brushUV.y < 0 || brushUV.y > 1 )
-				pixelColor = vec4(0);
-
-		}
-	}
-}

+ 55 - 29
hrt/prefab/terrain/Terrain.hx

@@ -16,28 +16,36 @@ typedef SurfaceProps = {
 @:access(hrt.prefab.terrain.TerrainMesh)
 class Terrain extends Object3D {
 
-	public var tileSize = 64.0;
-	public var cellSize = 1.0;
+	public var terrain : TerrainMesh;
+
+	// Tile Param
+	public var tileSize = 64.0; // Size of the tile
+	public var cellSize = 1.0; // Size of a quad of the Tile, cellCount = tileSize / cellSize
+	// Texture Param
 	public var heightMapResolution : Int = 64;
 	public var weightMapResolution : Int = 128;
-	public var autoCreateTile = false;
-	var tmpSurfacesProps : Array<SurfaceProps> = [];
-	public var terrain : TerrainMesh;
+	// Parallax Param
 	var parallaxAmount = 0.0;
 	var parallaxMinStep : Int = 1;
 	var parallaxMaxStep : Int = 16;
+	// Blend Param
 	var heightBlendStrength : Float = 0.0;
 	var blendSharpness : Float = 0.0;
-	var unpackWeight = new h3d.pass.ScreenFx(new UnpackWeight());
-
+	// Shadows Param
+	var castShadows = false;
+	// Data for binary save/load
 	var surfaceCount = 0;
 	var surfaceSize = 0;
+	// Utility
+	var tmpSurfacesProps : Array<SurfaceProps> = [];
+	var unpackWeight = new h3d.pass.ScreenFx(new UnpackWeight());
 
 	#if editor
 	var packWeight = new h3d.pass.ScreenFx(new PackWeight());
 	var editor : hide.prefab.terrain.TerrainEditor;
 	var cachedInstance : TerrainMesh;
 	public var showChecker = false;
+	public var autoCreateTile = false;
 	#end
 
 	public function new( ?parent ) {
@@ -57,10 +65,11 @@ class Terrain extends Object3D {
 		parallaxMaxStep = obj.parallaxMaxStep == null ? 1 : obj.parallaxMaxStep;
 		heightBlendStrength = obj.heightBlendStrength == null ? 0 : obj.heightBlendStrength;
 		blendSharpness = obj.blendSharpness == null ? 0 : obj.blendSharpness;
-		autoCreateTile = obj.autoCreateTile == null ? false : obj.autoCreateTile;
 		surfaceCount = obj.surfaceCount == null ? 0 : obj.surfaceCount;
 		surfaceSize = obj.surfaceSize == null ? 0 : obj.surfaceSize;
+		castShadows = obj.castShadows == null ? false : obj.castShadows;
 		#if editor
+		autoCreateTile = obj.autoCreateTile == null ? false : obj.autoCreateTile;
 		showChecker = obj.showChecker == null ? false : obj.showChecker;
 		#end
 	}
@@ -76,7 +85,7 @@ class Terrain extends Object3D {
 		obj.parallaxMaxStep = parallaxMaxStep;
 		obj.heightBlendStrength = heightBlendStrength;
 		obj.blendSharpness = blendSharpness;
-		obj.autoCreateTile = autoCreateTile;
+		obj.castShadows = castShadows;
 		var surfacesProps : Array<SurfaceProps> = [];
 		for(surface in terrain.surfaces){
 			var surfaceProps : SurfaceProps =
@@ -98,6 +107,7 @@ class Terrain extends Object3D {
 		obj.surfaceSize = terrain.surfaces.length == 0 ? 0 : terrain.surfaceArray.albedo.width;
 
 		#if editor
+		obj.autoCreateTile = autoCreateTile;
 	 	obj.showChecker = terrain.showChecker;
 		if( editor != null && terrain.surfaces.length > 0 ) editor.saveTextures();
 		#end
@@ -121,6 +131,13 @@ class Terrain extends Object3D {
 			if( x == null || y == null ) continue;
 			var type = coords[2];
 			var tile = terrain.createTile(x, y, false);
+			tile.material.shadows = castShadows;
+
+			#if editor
+			tile.material.mainPass.stencil = new h3d.mat.Stencil();
+			tile.material.mainPass.stencil.setFunc(Always, 0x01, 0x01, 0x01);
+			tile.material.mainPass.stencil.setOp(Keep, Keep, Replace);
+			#end
 
 			switch( type ) {
 				case "n":
@@ -209,6 +226,7 @@ class Terrain extends Object3D {
 			normal.waitLoad(wait);
 			pbr.waitLoad(wait);
 		}
+		tmpSurfacesProps = null;
 	}
 
 	public function initTerrain( ctx : Context, height = true, surface = true ) {
@@ -416,6 +434,14 @@ class Terrain extends Object3D {
 		terrain.heightBlendStrength = heightBlendStrength;
 		terrain.blendSharpness = blendSharpness;
 		terrain.showChecker = showChecker;
+
+		if( propName == "castShadows" ) {
+			if( terrain != null ) {
+				for( t in terrain.tiles )
+					t.material.castShadows = castShadows;
+			}
+		}
+
 		if( editor != null )
 			editor.update(propName);
 		#end
@@ -453,32 +479,32 @@ class Terrain extends Object3D {
 		editor.editContext = ctx;
 		editor.setupUI(props, ctx);
 		props.append('
-			<div class="group" name="Terrain"><dl>
-				<dt>Show Grid</dt><dd><input type="checkbox" field="terrain.showGrid"/></dd>
-				<dt>Visible</dt><dd><input type="checkbox" field="visible"/></dd>
-				<dt>Mode</dt>
-				<dd><select field="editor.renderMode">
-					<option value="PBR">PBR</option>
-					<option value="ShaderComplexity">Shader Complexity</option>
-					<option value="Checker">Checker</option>
-				</select></dd>
+			<div class="group" name="Rendering"><dl>
+				<dt>Cast Shadows</dt><dd><input type="checkbox" field="castShadows"/></dd>
+				<dt>Height Blend</dt><dd><input type="range" min="0" max="1" field="heightBlendStrength"/></dd>
+				<dt>Sharpness</dt><dd><input type="range" min="0" max="1" field="blendSharpness"/></dd>
 			</dl></div>
-			<div class="group" name="Quality">
+			<div class="group" name="Parallax"><dl>
+				<dt>Amount</dt><dd><input type="range" min="0" max="1" field="parallaxAmount"/></dd>
+				<dt>Min Step</dt><dd><input type="range" min="1" max="64" value="0" step="1" field="parallaxMinStep"/></dd>
+				<dt>Max Step</dt><dd><input type="range" min="1" max="64" value="0" step="1" field="parallaxMaxStep"/></dd>
+			</dl></div>
+			<div class="group" name="Quality"><dl>
 				<dt>Tile Size</dt><dd><input type="range" min="1" max="100" value="0" field="tileSizeSet"/></dd>
 				<dt>Cell Size</dt><dd><input type="range" min="0.01" max="10" value="0" field="cellSizeSet"/></dd>
 				<dt>WeightMap Resolution</dt><dd><input type="range" min="1" max="256" value="0" step="1" field="weightMapResolutionSet"/></dd>
 				<dt>HeightMap Resolution</dt><dd><input type="range" min="1" max="256" value="0" step="1" field="heightMapResolutionSet"/></dd>
 				<div align="center"><input type="button" value="Apply" class="apply"/></div>
-			</div>
-			<div class="group" name="Blend">
-				<dt>Height Blend</dt><dd><input type="range" min="0" max="1" field="heightBlendStrength"/></dd>
-				<dt>Sharpness</dt><dd><input type="range" min="0" max="1" field="blendSharpness"/></dd>
-			</div>
-			<div class="group" name="Parallax">
-				<dt>Amount</dt><dd><input type="range" min="0" max="1" field="parallaxAmount"/></dd>
-				<dt>Min Step</dt><dd><input type="range" min="1" max="64" value="0" step="1" field="parallaxMinStep"/></dd>
-				<dt>Max Step</dt><dd><input type="range" min="1" max="64" value="0" step="1" field="parallaxMaxStep"/></dd>
-			</div>
+			</dl></div>
+			<div class="group" name="Debug"><dl>
+				<dt>Show Grid</dt><dd><input type="checkbox" field="terrain.showGrid"/></dd>
+				<dt>Mode</dt>
+				<dd><select field="editor.renderMode">
+						<option value="PBR">PBR</option>
+						<option value="ShaderComplexity">Shader Complexity</option>
+						<option value="Checker">Checker</option>
+					</select></dd>
+			</dl></div>
 		');
 
 		props.find(".apply").click(function(_) {

+ 10 - 3
hrt/prefab/terrain/TerrainMesh.hx

@@ -237,9 +237,16 @@ class TerrainMesh extends h3d.scene.Object {
 			var minTileX = Math.floor((pos.x - range)/ tileSize);
 			var maxTileY = Math.floor((pos.y + range)/ tileSize);
 			var minTileY = Math.floor((pos.y - range)/ tileSize);
-			for( x in minTileX ... maxTileX + 1)
-				for( y in minTileY...maxTileY + 1)
-					createTile(x, y);
+			for( x in minTileX ... maxTileX + 1) {
+				for( y in minTileY...maxTileY + 1) {
+					var t = createTile(x, y);
+					#if editor
+					t.material.mainPass.stencil = new h3d.mat.Stencil();
+					t.material.mainPass.stencil.setFunc(Always, 0x01, 0x01, 0x01);
+					t.material.mainPass.stencil.setOp(Keep, Keep, Replace);
+					#end
+				}
+			}
 		}
 		var result : Array<Tile> = [];
 		for( tile in tiles)

+ 0 - 1
hrt/prefab/terrain/Tile.hx

@@ -31,7 +31,6 @@ class Tile extends h3d.scene.Mesh {
 		shader = new hrt.shader.Terrain();
 		material.mainPass.addShader(shader);
 		material.mainPass.culling = Back;
-		material.shadows = false;
 		this.x = x * terrain.tileSize;
 		this.y = y * terrain.tileSize;
 		name = "tile_" + x + "_" + y;