瀏覽代碼

Update Terrain :
- Can create rectangular Tile
- BugFix normal blend
- Automatic heightMap size
- "Weightmap Resolution" is now "Pixel per Unit"
- "Heightmap Resolution" is now "Vertex per Unit"

ShiroSmith 5 年之前
父節點
當前提交
7e705d6448

+ 7 - 7
hide/prefab/terrain/Brush.hx

@@ -26,19 +26,19 @@ class Brush {
 		return ( brushMode.mode == Delete || (bitmap != null && tex != null && name != null && step > 0.0 && texPath != null) );
 	}
 
-	public function scaleForTex( tileSize : Float, texResolution : Float ) {
+	public function scaleForTex( tileSize : h2d.col.Point, texResolution : h2d.col.IPoint ) {
 		if( tex != null ) {
-			var scale = size / ((tileSize / texResolution) * tex.width);
-			bitmap.setScale(scale);
+			bitmap.scaleX = size / ((tileSize.x / texResolution.x) * tex.width);
+			bitmap.scaleY = size / ((tileSize.y / texResolution.y) * tex.height);
 		}
 	}
 
-	public function drawTo( target : h3d.mat.Texture, pos : h3d.Vector, tileSize : Float, ?offset = 0 ) {
-		var texSize = target.width + offset;
+	public function drawTo( target : h3d.mat.Texture, pos : h3d.Vector, tileSize : h2d.col.Point, ?offset = 0 ) {
+		var texSize = new h2d.col.IPoint(target.width + offset, target.height + offset);
 		scaleForTex(tileSize, texSize);
 		bitmap.setPosition(
-						(pos.x * texSize - ( size / (tileSize / texSize) * 0.5 )),
-						(pos.y * texSize - ( size / (tileSize / texSize) * 0.5 )));
+						(pos.x * texSize.x - ( size / (tileSize.x / texSize.x) * 0.5 )),
+						(pos.y * texSize.y - ( size / (tileSize.y / texSize.y) * 0.5 )));
 		bitmap.drawTo(target);
 	}
 }

+ 4 - 4
hide/prefab/terrain/CustomUV.hx

@@ -7,16 +7,16 @@ class CustomUV extends hxsl.Shader {
 		@:import h3d.shader.BaseMesh;
 		@param var tileIndex : Float;
 
-		@param var heightMapSize : Float;
+		@param var heightMapSize : Vec2;
 		@param var heightMap : Sampler2D;
-		@param var primSize : Float;
+		@param var primSize : Vec2;
 
 		var calculatedUV : Vec2;
 		var terrainUV : Vec2;
 
 		function vertex() {
-			terrainUV = input.position.xy / primSize * (heightMapSize - 1) / heightMapSize + 0.5 / heightMapSize;
-			calculatedUV = input.position.xy / primSize;
+			terrainUV = input.position.xy / primSize.xy * (heightMapSize.xy - 1) / heightMapSize.xy + 0.5 / heightMapSize.xy;
+			calculatedUV = input.position.xy / primSize.xy;
 			transformedPosition += (vec3(0,0, heightMap.get(terrainUV).r) * global.modelView.mat3());
 		}
 

+ 9 - 9
hide/prefab/terrain/StrokeBuffer.hx

@@ -9,10 +9,10 @@ class StrokeBuffer {
 	public var tempTex : h3d.mat.Texture;
 	public var format : hxd.PixelFormat;
 
-	public function new(size, x, y, format){
+	public function new(size : h2d.col.IPoint, x, y, format){
 		this.format = format;
-		tex = new h3d.mat.Texture(size,size, [Target], format);
-		tempTex = new h3d.mat.Texture(size,size, [Target], format);
+		tex = new h3d.mat.Texture(size.x, size.y, [Target], format);
+		tempTex = new h3d.mat.Texture(size.x, size.y, [Target], format);
 		tex.filter = Linear;
 		tempTex.filter = Linear;
 		this.x = x;
@@ -25,11 +25,11 @@ class StrokeBuffer {
 		if(tempTex != null) tempTex.dispose();
 	}
 
-	public function refresh(size){
+	public function refresh(size: h2d.col.IPoint){
 		if(tex != null) tex.dispose();
 		if(tempTex != null) tempTex.dispose();
-		tex = new h3d.mat.Texture(size, size, [Target], format);
-		tempTex = new h3d.mat.Texture(size, size, [Target], format);
+		tex = new h3d.mat.Texture(size.x, size.y, [Target], format);
+		tempTex = new h3d.mat.Texture(size.x, size.y, [Target], format);
 		tex.filter = Linear;
 		tempTex.filter = Linear;
 		tex.preventAutoDispose();
@@ -48,10 +48,10 @@ class StrokeBuffer {
 class StrokeBufferArray{
 
 	public var strokeBuffers(default, null) : Array<StrokeBuffer> = [];
-	var texSize = 0;
+	var texSize = new h2d.col.IPoint(0,0);
 	var format : hxd.PixelFormat;
 
-	public function new(format, texSize){
+	public function new(format, texSize : h2d.col.IPoint ) {
 		this.format = format;
 		this.texSize = texSize;
 	}
@@ -72,7 +72,7 @@ class StrokeBufferArray{
 		return strokeBuffer;
 	}
 
-	public function refresh(size){
+	public function refresh( size : h2d.col.IPoint ){
 		if(texSize == size) return;
 		texSize = size;
 		for(strokeBuffer in strokeBuffers)

+ 24 - 22
hide/prefab/terrain/TerrainEditor.hx

@@ -83,8 +83,8 @@ class TerrainEditor {
 
 		brushPreview = new hide.prefab.terrain.Brush.BrushPreview(terrainPrefab.terrain);
 
-		heightStrokeBufferArray = new hide.prefab.terrain.StrokeBuffer.StrokeBufferArray(RGBA32F, terrainPrefab.heightMapResolution + 1);
-		weightStrokeBufferArray = new hide.prefab.terrain.StrokeBuffer.StrokeBufferArray(R8, terrainPrefab.weightMapResolution);
+		heightStrokeBufferArray = new hide.prefab.terrain.StrokeBuffer.StrokeBufferArray(RGBA32F,new h2d.col.IPoint(terrainPrefab.terrain.heightMapResolution.x + 1, terrainPrefab.terrain.heightMapResolution.y + 1));
+		weightStrokeBufferArray = new hide.prefab.terrain.StrokeBuffer.StrokeBufferArray(R8, terrainPrefab.terrain.weightMapResolution);
 		customScene.renderer = customRenderer;
 		#if debug
 		customScene.checkPasses = false;
@@ -200,8 +200,8 @@ class TerrainEditor {
 	}
 
 	public function refresh() {
-		heightStrokeBufferArray.refresh(terrainPrefab.heightMapResolution + 1);
-		weightStrokeBufferArray.refresh(terrainPrefab.weightMapResolution);
+		heightStrokeBufferArray.refresh(new h2d.col.IPoint(terrainPrefab.terrain.heightMapResolution.x + 1, terrainPrefab.terrain.heightMapResolution.y + 1));
+		weightStrokeBufferArray.refresh(terrainPrefab.terrain.weightMapResolution);
 	}
 
 	function updateRender() {
@@ -253,8 +253,8 @@ class TerrainEditor {
 			p.depthWrite = true;
 			tile.material.addPass(p);
 			var s = new hide.prefab.terrain.CustomUV();
-			s.primSize = terrainPrefab.terrain.tileSize;
-			s.heightMapSize = terrainPrefab.heightMapResolution;
+			s.primSize.set(terrainPrefab.terrain.tileSize.x, terrainPrefab.terrain.tileSize.y);
+			s.heightMapSize.set(terrainPrefab.terrain.heightMapResolution.x, terrainPrefab.terrain.heightMapResolution.y);
 			s.heightMap = tile.heightMap;
 			s.tileIndex = i;
 			p.addShader(s);
@@ -549,9 +549,9 @@ class TerrainEditor {
 		var worldPos = screenToWorld(mouseX, mouseY, ctx).toVector();
 		if( currentBrush.brushMode.snapToGrid ) {
 			var localPos = terrainPrefab.terrain.globalToLocal(worldPos.clone());
-			localPos.x = hxd.Math.round(localPos.x / terrainPrefab.terrain.cellSize) * terrainPrefab.terrain.cellSize;
-			localPos.y = hxd.Math.round(localPos.y / terrainPrefab.terrain.cellSize) * terrainPrefab.terrain.cellSize;
-			localPos.z = hxd.Math.round(localPos.z / terrainPrefab.terrain.cellSize) * terrainPrefab.terrain.cellSize;
+			localPos.x = hxd.Math.round(localPos.x / terrainPrefab.terrain.cellSize.x) * terrainPrefab.terrain.cellSize.x;
+			localPos.y = hxd.Math.round(localPos.y / terrainPrefab.terrain.cellSize.y) * terrainPrefab.terrain.cellSize.y;
+			localPos.z = localPos.z;
 			worldPos = terrainPrefab.terrain.globalToLocal(localPos.clone());
 		}
 		return worldPos;
@@ -568,7 +568,7 @@ class TerrainEditor {
 		var tiles = terrainPrefab.terrain.tiles;
 		for( i in 0 ... tiles.length )
 			if( hxd.Math.ceil(pixel.z) == i )
-				brushWorldPos = tiles[i].localToGlobal(new h3d.Vector(pixel.x * terrainPrefab.tileSize, pixel.y * terrainPrefab.tileSize, 0));
+				brushWorldPos = tiles[i].localToGlobal(new h3d.Vector(pixel.x * terrainPrefab.tileSizeX, pixel.y * terrainPrefab.tileSizeY, 0));
 		return brushWorldPos;
 	}
 
@@ -652,7 +652,7 @@ class TerrainEditor {
 		var brushWorldPos = uvTexPixels == null ? pos : getBrushWorldPosFromTex(pos, ctx);
 		if( brushWorldPos == null ) return;
 		var c = terrainPrefab.terrain.tiles.length;
-		var tiles = terrainPrefab.terrain.getTiles(pos.x, pos.y, currentBrush.size / 2.0, autoCreateTile);
+		var tiles = terrainPrefab.terrain.getTiles(brushWorldPos.x, brushWorldPos.y, currentBrush.size / 2.0, autoCreateTile);
 		if( c != terrainPrefab.terrain.tiles.length ) {
 			renderTerrainUV(ctx);
 			brushWorldPos = getBrushWorldPosFromTex(pos, ctx);
@@ -663,7 +663,7 @@ class TerrainEditor {
 		if( shader == null ) shader = currentBrush.bitmap.addShader(new hrt.shader.Brush());
 		currentBrush.bitmap.blendMode = currentBrush.brushMode.accumulate ? Add : Max;
 		shader.strength = currentBrush.strength;
-		shader.size = currentBrush.size / terrainPrefab.tileSize;
+		shader.size.set(currentBrush.size / terrainPrefab.tileSizeX, currentBrush.size / terrainPrefab.tileSizeY);
 
 		for( tile in tiles ) {
 			var strokeBuffer = weightStrokeBufferArray.getStrokeBuffer(tile.tileX, tile.tileY);
@@ -677,9 +677,10 @@ class TerrainEditor {
 				strokeBuffer.used = true;
 			}
 			var localPos = tile.globalToLocal(brushWorldPos.clone());
-			localPos.scale3(1.0 / terrainPrefab.tileSize);
-			shader.pos = new h3d.Vector(localPos.x - (currentBrush.size  / terrainPrefab.tileSize * 0.5), localPos.y - (currentBrush.size  / terrainPrefab.tileSize * 0.5));
-			currentBrush.drawTo(strokeBuffer.tex, localPos, terrainPrefab.tileSize);
+			localPos.x *= 1.0 / terrainPrefab.tileSizeX;
+			localPos.y *= 1.0 / terrainPrefab.tileSizeY;
+			shader.pos = new h3d.Vector(localPos.x - (currentBrush.size  / terrainPrefab.tileSizeX * 0.5), localPos.y - (currentBrush.size  / terrainPrefab.tileSizeY * 0.5));
+			currentBrush.drawTo(strokeBuffer.tex, localPos, terrainPrefab.terrain.tileSize );
 		}
 	}
 
@@ -696,7 +697,7 @@ class TerrainEditor {
 		var shader : hrt.shader.Brush = currentBrush.bitmap.getShader(hrt.shader.Brush);
 		if( shader == null ) shader = currentBrush.bitmap.addShader(new hrt.shader.Brush());
 		currentBrush.bitmap.color = new h3d.Vector(1.0);
-		shader.size = currentBrush.size / terrainPrefab.tileSize;
+		shader.size.set(currentBrush.size / terrainPrefab.tileSizeX, currentBrush.size / terrainPrefab.tileSizeY);
 
 		switch( currentBrush.brushMode.mode ) {
 			case AddSub :
@@ -713,15 +714,16 @@ class TerrainEditor {
 
 		for( tile in tiles ) {
 			var localPos = tile.globalToLocal(brushWorldPos.clone());
-			localPos.scale3(1.0 / terrainPrefab.tileSize);
+			localPos.x *= 1.0 / terrainPrefab.tileSizeX;
+			localPos.y *= 1.0 / terrainPrefab.tileSizeY;
 			var strokeBuffer = heightStrokeBufferArray.getStrokeBuffer(tile.tileX, tile.tileY);
 			if( strokeBuffer.used == false ) {
 				strokeBuffer.prevTex = tile.heightMap;
 				tile.heightMap = strokeBuffer.tempTex;
 				strokeBuffer.used = true;
 			}
-			shader.pos = new h3d.Vector(localPos.x - (currentBrush.size  / terrainPrefab.tileSize * 0.5), localPos.y - (currentBrush.size  / terrainPrefab.tileSize * 0.5));
-			currentBrush.drawTo(strokeBuffer.tex, localPos, terrainPrefab.tileSize, -1);
+			shader.pos = new h3d.Vector(localPos.x - (currentBrush.size  / terrainPrefab.tileSizeX * 0.5), localPos.y - (currentBrush.size  / terrainPrefab.tileSizeY * 0.5));
+			currentBrush.drawTo(strokeBuffer.tex, localPos, terrainPrefab.terrain.tileSize, -1);
 		}
 	}
 
@@ -855,7 +857,7 @@ class TerrainEditor {
 		swapIndex.shader.INDEX_COUNT = oldIndexes.length;
 		swapIndex.shader.oldIndexes = oldIndexes;
 		swapIndex.shader.newIndexes = newIndexes;
-		var newSurfaceIndexMap = new h3d.mat.Texture(terrainPrefab.weightMapResolution, terrainPrefab.weightMapResolution, [Target], RGBA);
+		var newSurfaceIndexMap = new h3d.mat.Texture(terrainPrefab.terrain.weightMapResolution.x, terrainPrefab.terrain.weightMapResolution.y, [Target], RGBA);
 		for( i in 0 ... terrainPrefab.terrain.tiles.length ) {
 			var tile = terrainPrefab.terrain.tiles[i];
 			var revert = tileRevertDatas[i];
@@ -911,7 +913,7 @@ class TerrainEditor {
 				tile.surfaceWeights = new Array<h3d.mat.Texture>();
 				tile.surfaceWeights = [for( i in 0...terrainPrefab.terrain.surfaces.length ) null];
 				for( i in 0 ... tile.surfaceWeights.length ) {
-					tile.surfaceWeights[i] = new h3d.mat.Texture(terrainPrefab.weightMapResolution, terrainPrefab.weightMapResolution, [Target], R8);
+					tile.surfaceWeights[i] = new h3d.mat.Texture(terrainPrefab.terrain.weightMapResolution.x, terrainPrefab.terrain.weightMapResolution.y, [Target], R8);
 					tile.surfaceWeights[i].wrap = Clamp;
 					tile.surfaceWeights[i].preventAutoDispose();
 				}
@@ -1058,7 +1060,7 @@ class TerrainEditor {
 		<div class="title">Surface <input type="button" style="font-weight:bold" id="addSurface" value="+"/></div>
 		<div class="terrain-surfaces"></div>
 		<div class="group" name="Params">
-			<dt>Tilling</dt><dd><input type="range" min="0" max="20" field="editor.currentSurface.tilling"/></dd>
+			<dt>Tilling</dt><dd><input type="range" min="0" max="2" field="editor.currentSurface.tilling"/></dd>
 			<dt>Offset X</dt><dd><input type="range" min="0" max="1" field="editor.currentSurface.offset.x"/></dd>
 			<dt>Offset Y</dt><dd><input type="range" min="0" max="1" field="editor.currentSurface.offset.y"/></dd>
 			<dt>Rotate</dt><dd><input type="range" min="0" max="360" field="editor.currentSurface.angle"/></dd>

+ 94 - 66
hrt/prefab/terrain/Terrain.hx

@@ -19,11 +19,11 @@ class Terrain extends Object3D {
 	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
+	public var tileSizeX : Float = 64.0;
+	public var tileSizeY : Float = 64.0;
+	public var vertexPerMeter : Float = 1.0;
 	// Texture Param
-	public var heightMapResolution : Int = 64;
-	public var weightMapResolution : Int = 128;
+	public var weightMapPixelPerMeter : Float = 1.0;
 	// Parallax Param
 	public var parallaxAmount = 0.0;
 	public var parallaxMinStep : Int = 1;
@@ -50,6 +50,12 @@ class Terrain extends Object3D {
 	public var brushOpacity : Float;
 	#end
 
+	// Backward Compatibility
+	var oldHeightMapResolution : Int = -1;
+	var oldWeightMapResolution : Int = -1;
+	var oldCellSize : Float = -1;
+	var needFormatUpdate = false;
+
 	public function new( ?parent ) {
 		super(parent);
 		type = "terrain";
@@ -57,10 +63,20 @@ class Terrain extends Object3D {
 
 	override function load( obj : Dynamic ) {
 		super.load(obj);
-		tileSize = obj.tileSize == null ? 1 : obj.tileSize;
-		cellSize = obj.cellSize == null ? 1 : obj.cellSize;
-		heightMapResolution = obj.heightMapResolution == null ? 1 : hxd.Math.ceil(obj.heightMapResolution);
-		weightMapResolution = obj.weightMapResolution == null ? 1 : hxd.Math.ceil(obj.weightMapResolution);
+		// Backward Compatibility
+		if( obj.cellSize != null ) oldCellSize = obj.cellSize;
+		if( obj.heightMapResolution != null ) oldHeightMapResolution = hxd.Math.ceil(obj.heightMapResolution);
+		if( obj.weightMapResolution != null ) oldWeightMapResolution = hxd.Math.ceil(obj.weightMapResolution);
+		if( obj.tileSize != null ) {
+			tileSizeX = obj.tileSize;
+			tileSizeY = obj.tileSize;
+		}
+		else {
+			tileSizeX = obj.tileSizeX == null ? 1 : obj.tileSizeX;
+			tileSizeY = obj.tileSizeY == null ? 1 : obj.tileSizeY;
+		}
+		if( obj.vertexPerMeter != null ) vertexPerMeter = obj.vertexPerMeter;
+		if( obj.weightMapPixelPerMeter != null ) weightMapPixelPerMeter = obj.weightMapPixelPerMeter;
 		if( obj.surfaces != null ) tmpSurfacesProps = obj.surfaces;
 		parallaxAmount = obj.parallaxAmount == null ? 0.0 : obj.parallaxAmount;
 		parallaxMinStep = obj.parallaxMinStep == null ? 1 : obj.parallaxMinStep;
@@ -79,10 +95,10 @@ class Terrain extends Object3D {
 
 	override function save() {
 		var obj : Dynamic = super.save();
-		if( tileSize > 0 ) obj.tileSize = tileSize;
-		if( cellSize > 0 ) obj.cellSize = cellSize;
-		if( heightMapResolution > 0 ) obj.heightMapResolution = hxd.Math.ceil(heightMapResolution);
-		if( weightMapResolution > 0 ) obj.weightMapResolution = hxd.Math.ceil(weightMapResolution);
+		obj.tileSizeX = tileSizeX;
+		obj.tileSizeY = tileSizeY;
+		obj.vertexPerMeter = vertexPerMeter;
+		obj.weightMapPixelPerMeter = weightMapPixelPerMeter;
 		obj.parallaxAmount = parallaxAmount;
 		obj.parallaxMinStep = parallaxMinStep;
 		obj.parallaxMaxStep = parallaxMaxStep;
@@ -113,7 +129,7 @@ class Terrain extends Object3D {
 			obj.surfaces = surfacesProps;
 		}
 		else {
-			// When cloning 
+			// When cloning
 			obj.surfaces = tmpSurfacesProps;
 			obj.surfaceCount = tmpSurfacesProps.length;
 		}
@@ -140,7 +156,7 @@ class Terrain extends Object3D {
 			return;
 
 		// Avoid texture alloc for unpacking
-		var tmpPackedWeightTexture = new h3d.mat.Texture(terrain.weightMapResolution, terrain.weightMapResolution, [Target]);
+		var tmpPackedWeightTexture = new h3d.mat.Texture(terrain.weightMapResolution.x, terrain.weightMapResolution.y, [Target]);
 
 		for( res in resDir ) {
 			var fileInfos = res.name.split(".");
@@ -169,7 +185,7 @@ class Terrain extends Object3D {
 				case "h":
 				if( height ) {
 					var bytes = res.entry.getBytes();
-					var pixels : hxd.Pixels.PixelsFloat = new hxd.Pixels(heightMapResolution + 1, heightMapResolution + 1, bytes, RGBA32F);
+					var pixels : hxd.Pixels.PixelsFloat = new hxd.Pixels(terrain.heightMapResolution.x + 1, terrain.heightMapResolution.y + 1, bytes, RGBA32F);
 					@:privateAccess tile.heightmapPixels = pixels;
 					#if editor
 					if( tile.heightMap == null ) @:privateAccess tile.refreshHeightMap();
@@ -185,7 +201,7 @@ class Terrain extends Object3D {
 						tile.packedWeightMapPixel = tmpPackedWeightTexture.capturePixels();
 						weightAsPNG.dispose();
 					} else {
-						var pixels : hxd.Pixels = new hxd.Pixels(terrain.weightMapResolution, terrain.weightMapResolution, res.entry.getBytes(), RGBA);
+						var pixels : hxd.Pixels = new hxd.Pixels(terrain.weightMapResolution.x, terrain.weightMapResolution.y, res.entry.getBytes(), RGBA);
 						tmpPackedWeightTexture.uploadPixels(pixels);
 						tile.packedWeightMapPixel = pixels;
 					}
@@ -228,7 +244,7 @@ class Terrain extends Object3D {
 						indexAsPNG.dispose();
 					}
 					else {
-						var pixels : hxd.Pixels = new hxd.Pixels(terrain.weightMapResolution, terrain.weightMapResolution, res.entry.getBytes(), RGBA);
+						var pixels : hxd.Pixels = new hxd.Pixels(terrain.weightMapResolution.x, terrain.weightMapResolution.y, res.entry.getBytes(), RGBA);
 						tile.indexMapPixels = pixels;
 						tile.surfaceIndexMap.uploadPixels(pixels);
 					}
@@ -324,8 +340,27 @@ class Terrain extends Object3D {
 		//#else
 		//loadBinary(ctx);
 		//#end
-	}
 
+		// Backward Compatibility
+		if( needFormatUpdate ) {
+
+			for( s in terrain.surfaces )
+				s.tilling /= tileSizeX;
+			terrain.updateSurfaceParams();
+			#if editor
+			// Need to create a terrain with the new params before saving
+			terrain.weightMapResolution = new h2d.col.IPoint(Math.round(tileSizeX * weightMapPixelPerMeter), Math.round(tileSizeY * weightMapPixelPerMeter));
+			terrain.cellCount = new h2d.col.IPoint(Math.ceil(tileSizeX * vertexPerMeter), Math.ceil(tileSizeY * vertexPerMeter) );
+			terrain.cellSize = new h2d.col.Point(tileSizeX / terrain.cellCount.x, tileSizeY / terrain.cellCount.y );
+			terrain.heightMapResolution = new h2d.col.IPoint(terrain.cellCount.x + 1, terrain.cellCount.y + 1);
+			terrain.refreshAllGrids();
+			terrain.refreshAllTex();
+			for( tile in terrain.tiles )
+				tile.blendEdges();
+			modified = true;
+			#end
+		}
+	}
 
 	#if editor
 
@@ -359,7 +394,7 @@ class Terrain extends Object3D {
 	}
 
 	public function saveWeightTextures( ctx : Context ) {
-		var packedWeightsTex = new h3d.mat.Texture(terrain.weightMapResolution, terrain.weightMapResolution, [Target], RGBA);
+		var packedWeightsTex = new h3d.mat.Texture(terrain.weightMapResolution.x, terrain.weightMapResolution.y, [Target], RGBA);
 		for( tile in terrain.tiles ) {
 			h3d.Engine.getCurrent().pushTarget(packedWeightsTex);
 			packWeight.shader.indexMap = tile.surfaceIndexMap;
@@ -447,28 +482,33 @@ class Terrain extends Object3D {
 	override function makeInstance( ctx : Context ) : Context {
 		ctx = ctx.clone(this);
 
-
-		/*#if editor
-		// Attach the terrain to the scene directly, avoid slow refresh
-		if( cachedInstance != null ) {
-			ctx.local3d = terrain;
-			ctx.local3d.name = name;
-			updateInstance(ctx);
-			return ctx;
+		terrain = new TerrainMesh(ctx.local3d);
+		terrain.tileSize = new h2d.col.Point(tileSizeX, tileSizeY);
+
+		// Backward Compatibility
+		if( oldHeightMapResolution != -1 && oldCellSize != -1 ) {
+			terrain.heightMapResolution = new h2d.col.IPoint(oldHeightMapResolution, oldHeightMapResolution);
+			var resolution = Math.max(0.1, oldCellSize);
+			var cellCount = Math.ceil(Math.min(1000, tileSizeX / resolution));
+			var finalCellSize = tileSizeX / cellCount;
+			terrain.cellCount = new h2d.col.IPoint(cellCount, cellCount);
+			terrain.cellSize = new h2d.col.Point(finalCellSize, finalCellSize);
+			vertexPerMeter = terrain.cellCount.x / tileSizeX;
+			needFormatUpdate = true;
 		}
 		else {
-			terrain = new TerrainMesh(ctx.local3d.getScene());
-			cachedInstance = terrain;
+			terrain.cellCount = new h2d.col.IPoint(Math.ceil(tileSizeX * vertexPerMeter), Math.ceil(tileSizeY * vertexPerMeter) );
+			terrain.cellSize = new h2d.col.Point(tileSizeX / terrain.cellCount.x, tileSizeY / terrain.cellCount.y );
+			terrain.heightMapResolution = new h2d.col.IPoint(terrain.cellCount.x + 1, terrain.cellCount.y + 1);
 		}
-		#else*/
-		terrain = new TerrainMesh(ctx.local3d);
-		//#end
+		if( oldWeightMapResolution != -1 ) {
+			terrain.weightMapResolution = new h2d.col.IPoint(oldWeightMapResolution, oldWeightMapResolution);
+			weightMapPixelPerMeter = oldWeightMapResolution / tileSizeX;
+			needFormatUpdate = true;
+		}
+		else
+			terrain.weightMapResolution = new h2d.col.IPoint(Math.round(tileSizeX * weightMapPixelPerMeter), Math.round(tileSizeY * weightMapPixelPerMeter));
 
-		terrain.cellCount = getCellCount();
-		terrain.cellSize = getCellSize();
-		terrain.tileSize = terrain.cellCount * terrain.cellSize;
-		terrain.heightMapResolution = heightMapResolution;
-		terrain.weightMapResolution = weightMapResolution;
 		terrain.parallaxAmount = parallaxAmount / 10;
 		terrain.parallaxMinStep = parallaxMinStep;
 		terrain.parallaxMaxStep = parallaxMaxStep;
@@ -512,18 +552,6 @@ class Terrain extends Object3D {
 		#end
 	}
 
-	public function getCellCount() {
-		var resolution = Math.max(0.1, cellSize);
-		var cellCount = Math.ceil(Math.min(1000, tileSize / resolution));
-		return cellCount;
-	}
-
-	public function getCellSize() {
-		var cellCount = getCellCount();
-		var finalCellSize = tileSize / cellCount;
-		return finalCellSize;
-	}
-
 	#if editor
 	override function makeInteractive( ctx : Context ) : h3d.scene.Interactive {
 		return null;
@@ -555,10 +583,10 @@ class Terrain extends Object3D {
 				<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>
+				<dt>Tile Size X</dt><dd><input type="range" min="1" max="100" value="0" field="tileSizeSetX"/></dd>
+				<dt>Tile Size Y</dt><dd><input type="range" min="1" max="100" value="0" field="tileSizeSetY"/></dd>
+				<dt>Vertex/Unit</dt><dd><input type="range" min="0.1" max="2" " value="0" field="vertexPerMeter"/></dd>
+				<dt>Pixel/Unit</dt><dd><input type="range" min="0.1" max="2" value="0" field="weightMapPixelPerMeter"/></dd>
 				<div align="center"><input type="button" value="Apply" class="apply"/></div>
 			</dl></div>
 			<div class="group" name="Debug"><dl>
@@ -573,16 +601,16 @@ class Terrain extends Object3D {
 		');
 
 		props.find(".apply").click(function(_) {
-			tileSize = @:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="tileSizeSet").range.value;
-			cellSize = @:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="cellSizeSet").range.value;
-			weightMapResolution = Std.int(@:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="weightMapResolutionSet").range.value);
-			heightMapResolution = Std.int(@:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="heightMapResolutionSet").range.value);
-			terrain.cellCount = getCellCount();
-			terrain.cellSize = getCellSize();
-			terrain.tileSize = terrain.cellCount * terrain.cellSize;
+			tileSizeX = @:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="tileSizeSetX").range.value;
+			tileSizeY = @:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="tileSizeSetY").range.value;
+			weightMapPixelPerMeter = Std.int(@:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="weightMapPixelPerMeter").range.value);
+			vertexPerMeter = @:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="vertexPerMeter").range.value;
+			terrain.weightMapResolution = new h2d.col.IPoint(Math.round(tileSizeX * weightMapPixelPerMeter), Math.round(tileSizeY * weightMapPixelPerMeter));
+			terrain.tileSize = new h2d.col.Point(tileSizeX, tileSizeY);
+			terrain.cellCount = new h2d.col.IPoint(Math.ceil(tileSizeX * vertexPerMeter), Math.ceil(tileSizeY * vertexPerMeter) );
+			terrain.cellSize = new h2d.col.Point(tileSizeX / terrain.cellCount.x, tileSizeY / terrain.cellCount.y );
+			terrain.heightMapResolution = new h2d.col.IPoint(terrain.cellCount.x + 1, terrain.cellCount.y + 1);
 			terrain.refreshAllGrids();
-			terrain.heightMapResolution = heightMapResolution;
-			terrain.weightMapResolution = weightMapResolution;
 			terrain.refreshAllTex();
 			for( tile in terrain.tiles )
 				tile.blendEdges();
@@ -597,10 +625,10 @@ class Terrain extends Object3D {
 		});
 
 		// Reset values if not applied
-		@:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="tileSizeSet").range.value = tileSize;
-		@:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="cellSizeSet").range.value = cellSize;
-		@:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="weightMapResolutionSet").range.value = weightMapResolution;
-		@:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="heightMapResolutionSet").range.value = heightMapResolution;
+		@:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="tileSizeSetX").range.value = tileSizeX;
+		@:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="tileSizeSetY").range.value = tileSizeY;
+		@:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="weightMapPixelPerMeter").range.value = weightMapPixelPerMeter;
+		@:privateAccess Lambda.find(ctx.properties.fields, f->f.fname=="vertexPerMeter").range.value = vertexPerMeter;
 	}
 	#end
 

+ 19 - 50
hrt/prefab/terrain/TerrainMesh.hx

@@ -4,11 +4,12 @@ package hrt.prefab.terrain;
 class TerrainMesh extends h3d.scene.Object {
 
 	// Resolution Vertexes/Pixels
-	public var tileSize : Float;
-	public var cellSize : Float;
-	public var cellCount : Int;
-	public var heightMapResolution : Int;
-	public var weightMapResolution : Int;
+	public var tileSize : h2d.col.Point;
+	public var cellSize : h2d.col.Point;
+	public var cellCount : h2d.col.IPoint;
+
+	public var heightMapResolution : h2d.col.IPoint;
+	public var weightMapResolution : h2d.col.IPoint;
 	public var useBigPrim = false;
 
 	// Shader Params
@@ -43,45 +44,13 @@ class TerrainMesh extends h3d.scene.Object {
 			surfaceArray.dispose();
 	}
 
-	public override function clone( ?o : h3d.scene.Object ) : h3d.scene.Object {
-		var o = new TerrainMesh();
-		o.tileSize = tileSize;
-		o.cellSize = cellSize;
-		o.cellCount = cellCount;
-		o.heightMapResolution = heightMapResolution;
-		o.weightMapResolution = weightMapResolution;
-		#if editor
-		o.showGrid = showGrid;
-		o.showChecker = showChecker;
-		o.showComplexity = showComplexity;
-		#end
-		o.parallaxAmount = parallaxAmount;
-		o.parallaxMinStep = parallaxMinStep;
-		o.parallaxMaxStep = parallaxMaxStep;
-		o.heightBlendStrength = heightBlendStrength;
-		o.blendSharpness = blendSharpness;
-
-		for( i in 0...tiles.length ) {
-			var t = Std.downcast(tiles[i].clone(), Tile);
-			t.parent = o;
-			o.tiles.push(t);
-		}
-
-		for( i in 0...surfaces.length )
-			o.surfaces.push(surfaces[i].clone());
-
-		o.surfaceArray = surfaceArray.clone();
-
-		return o;
-	}
-
 	public function getHeight( x : Float, y : Float, fast = false) : Float {
 		var z = 0.0;
 		var t = getTileAtWorldPos(x, y);
 		if( t != null ) {
 			tmpVec.set(x, y);
 			var pos = t.globalToLocal(tmpVec);
-			z = t.getHeight(pos.x / tileSize, pos.y / tileSize, fast);
+			z = t.getHeight(pos.x / tileSize.x, pos.y / tileSize.y, fast);
 		}
 		return z;
 	}
@@ -154,8 +123,8 @@ class TerrainMesh extends h3d.scene.Object {
 
 	public function refreshAllGrids() {
 		for( tile in tiles ) {
-			tile.x = tile.tileX * tileSize;
-			tile.y = tile.tileY * tileSize;
+			tile.x = tile.tileX * tileSize.x;
+			tile.y = tile.tileY * tileSize.y;
 			tile.refreshGrid();
 		}
 		for( tile in tiles )
@@ -235,8 +204,8 @@ class TerrainMesh extends h3d.scene.Object {
 	public function getTileAtWorldPos( x : Float, y : Float ) : Tile {
 		var pos = toLocalPos(x, y);
 		var result : Tile = null;
-		var tileX = Math.floor(pos.x / tileSize);
-		var tileY = Math.floor(pos.y / tileSize);
+		var tileX = Math.floor(pos.x / tileSize.x);
+		var tileY = Math.floor(pos.y / tileSize.y);
 		for( tile in tiles )
 			if( tile.tileX == tileX && tile.tileY == tileY ) result = tile;
 		return result;
@@ -244,8 +213,8 @@ class TerrainMesh extends h3d.scene.Object {
 
 	public function createTileAtWorldPos( x : Float, y : Float ) : Tile {
 		var pos = toLocalPos(x, y);
-		var tileX = Math.floor(pos.x / tileSize);
-		var tileY = Math.floor(pos.y / tileSize);
+		var tileX = Math.floor(pos.x / tileSize.x);
+		var tileY = Math.floor(pos.y / tileSize.y);
 		var result = getTile(tileX, tileY);
 		return result == null ? createTile(tileX, tileY) : result;
 	}
@@ -253,10 +222,10 @@ class TerrainMesh extends h3d.scene.Object {
 	public function getTiles( x : Float, y : Float, range : Float, ?create = false ) : Array<Tile> {
 		var pos = toLocalPos(x, y);
 		if( create != null && create ) {
-			var maxTileX = Math.floor((pos.x + range)/ tileSize);
-			var minTileX = Math.floor((pos.x - range)/ tileSize);
-			var maxTileY = Math.floor((pos.y + range)/ tileSize);
-			var minTileY = Math.floor((pos.y - range)/ tileSize);
+			var maxTileX = Math.floor((pos.x + range)/ tileSize.x);
+			var minTileX = Math.floor((pos.x - range)/ tileSize.x);
+			var maxTileY = Math.floor((pos.y + range)/ tileSize.y);
+			var minTileY = Math.floor((pos.y - range)/ tileSize.y);
 			for( x in minTileX ... maxTileX + 1) {
 				for( y in minTileY...maxTileY + 1) {
 					var t = createTile(x, y);
@@ -270,8 +239,8 @@ class TerrainMesh extends h3d.scene.Object {
 		}
 		var result : Array<Tile> = [];
 		for( tile in tiles)
-			if( Math.abs(pos.x - (tile.tileX * tileSize + tileSize * 0.5)) <= range + (tileSize * 0.5)
-			&& Math.abs(pos.y - (tile.tileY * tileSize + tileSize * 0.5)) <= range + (tileSize * 0.5) )
+			if( Math.abs(pos.x - (tile.tileX * tileSize.x + tileSize.x * 0.5)) <= range + (tileSize.x * 0.5)
+			&& Math.abs(pos.y - (tile.tileY * tileSize.y + tileSize.y * 0.5)) <= range + (tileSize.y * 0.5) )
 				result.push(tile);
 		return result;
 	}

+ 188 - 258
hrt/prefab/terrain/Tile.hx

@@ -1,5 +1,7 @@
 package hrt.prefab.terrain;
 
+import h3d.prim.Grid;
+
 enum Direction{
 	Up; Down; Left; Right; UpLeft; UpRight; DownLeft; DownRight;
 }
@@ -36,8 +38,8 @@ class Tile extends h3d.scene.Mesh {
 		shader = new hrt.shader.Terrain();
 		material.mainPass.addShader(shader);
 		material.mainPass.culling = Back;
-		this.x = x * terrain.tileSize;
-		this.y = y * terrain.tileSize;
+		this.x = x * terrain.tileSize.x;
+		this.y = y * terrain.tileSize.y;
 		name = "tile_" + x + "_" + y;
 		material.mainPass.setPassName("terrain");
 	}
@@ -79,8 +81,7 @@ class Tile extends h3d.scene.Mesh {
 	public function createBigPrim( bytes : haxe.io.Bytes ) {
 
 		var stride = 3 * 4 + 3 * 4; // Normal + Tangent
-		var vertexCount = (terrain.cellCount + 1) * (terrain.cellCount + 1);
-		var length = vertexCount * stride;
+		var vertexCount = (terrain.cellCount.x + 1) * (terrain.cellCount.y + 1);
 		if( bytes.length != stride * vertexCount ) {
 			throw "Bytes length doesn't match with the size of tiles";
 			return;
@@ -93,7 +94,7 @@ class Tile extends h3d.scene.Mesh {
 		bigPrim = new h3d.prim.BigPrimitive(9, true);
 		inline function addVertice(x : Float, y : Float, i : Int) {
 			// Pos
-			bigPrim.addPoint(x, y, getHeight(x / terrain.tileSize, y / terrain.tileSize)); // Use addPoint() instead of addVertexValue() for the bounds
+			bigPrim.addPoint(x, y, getHeight(x / terrain.tileSize.x, y / terrain.tileSize.y)); // Use addPoint() instead of addVertexValue() for the bounds
 			// Normal
 			bigPrim.addVertexValue(bytes.getFloat(i * stride));
 			bigPrim.addVertexValue(bytes.getFloat(i * stride + 4));
@@ -107,40 +108,36 @@ class Tile extends h3d.scene.Mesh {
 		var cellCount = terrain.cellCount;
 		var cellSize = terrain.cellSize;
 		bigPrim.begin(0,0);
-		for( y in 0 ... cellCount + 1 ) {
-			for( x in 0 ... cellCount + 1 ) {
-				addVertice(x * cellSize, y * cellSize, x + y * (terrain.cellCount + 1));
+		for( y in 0 ... cellCount.y + 1 ) {
+			for( x in 0 ... cellCount.x + 1 ) {
+				addVertice(x * cellSize.x, y * cellSize.y, x + y * (terrain.cellCount.x + 1));
 			}
 		}
 
-		for( y in 0 ... cellCount ) {
-			for( x in 0 ... cellCount ) {
-				var i = x + y * (cellCount + 1);
+		for( y in 0 ... cellCount.y ) {
+			for( x in 0 ... cellCount.x ) {
+				var i = x + y * (cellCount.x + 1);
 				bigPrim.addIndex(i);
 				bigPrim.addIndex(i + 1);
-				bigPrim.addIndex(i + cellCount + 2);
+				bigPrim.addIndex(i + cellCount.x + 2);
 				bigPrim.addIndex(i);
-				bigPrim.addIndex(i + cellCount + 2);
-				bigPrim.addIndex(i + cellCount + 1);
+				bigPrim.addIndex(i + cellCount.x + 2);
+				bigPrim.addIndex(i + cellCount.x + 1);
 			}
 		}
 		bigPrim.flush();
 		primitive = bigPrim;
 	}
 
-	public function computeTangents() {
-		if( grid != null )
-			grid.addTangents();
-	}
-
 	public function refreshGrid() {
 		if( bigPrim != null )
 			return;
-		if( grid == null || grid.width != terrain.cellCount || grid.height != terrain.cellCount || grid.cellWidth != terrain.cellSize || grid.cellHeight != terrain.cellSize ) {
+		if( grid == null || grid.width != terrain.cellCount.x || grid.height != terrain.cellCount.y || grid.cellWidth != terrain.cellSize.x || grid.cellHeight != terrain.cellSize.y ) {
 			if( grid != null ) grid.dispose();
-		 	grid = new h3d.prim.Grid(terrain.cellCount, terrain.cellCount, terrain.cellSize, terrain.cellSize);
+		 	grid = new h3d.prim.Grid(terrain.cellCount.x, terrain.cellCount.y, terrain.cellSize.x, terrain.cellSize.y);
 			primitive = grid;
 		}
+		computeHeight();
 		computeNormals();
 		computeTangents();
 	}
@@ -168,6 +165,8 @@ class Tile extends h3d.scene.Mesh {
         flags.set(Left);
 		flags.set(Up);
 		flags.set(UpLeft);
+
+		computeHeight();
 		computeEdgesHeight(flags);
 		computeNormals();
 		computeEdgesNormals();
@@ -175,9 +174,9 @@ class Tile extends h3d.scene.Mesh {
 	}
 
 	function refreshHeightMap() {
-		if( heightMap == null || heightMap.width != terrain.heightMapResolution + 1 ) {
+		if( heightMap == null || heightMap.width != terrain.heightMapResolution.x + 1 || heightMap.height != terrain.heightMapResolution.y + 1 ) {
 			var oldHeightMap = heightMap;
-			heightMap = new h3d.mat.Texture(terrain.heightMapResolution + 1, terrain.heightMapResolution + 1, [Target], RGBA32F );
+			heightMap = new h3d.mat.Texture(terrain.heightMapResolution.x + 1, terrain.heightMapResolution.y + 1, [Target], RGBA32F );
 			heightMap.setName("terrainHeightMap");
 			heightMap.wrap = Clamp;
 			heightMap.filter = Linear;
@@ -196,9 +195,9 @@ class Tile extends h3d.scene.Mesh {
 	}
 
 	function refreshIndexMap() {
-		if( surfaceIndexMap == null || surfaceIndexMap.width != terrain.weightMapResolution ) {
+		if( surfaceIndexMap == null || surfaceIndexMap.width != terrain.weightMapResolution.x || surfaceIndexMap.height != terrain.weightMapResolution.y ) {
 			var oldSurfaceIndexMap = surfaceIndexMap;
-			surfaceIndexMap = new h3d.mat.Texture(terrain.weightMapResolution, terrain.weightMapResolution, [Target], RGBA);
+			surfaceIndexMap = new h3d.mat.Texture(terrain.weightMapResolution.x, terrain.weightMapResolution.y, [Target], RGBA);
 			surfaceIndexMap.setName("terrainSurfaceIndexMap");
 			surfaceIndexMap.filter = Nearest;
 			surfaceIndexMap.preventAutoDispose();
@@ -215,12 +214,12 @@ class Tile extends h3d.scene.Mesh {
 	}
 
 	function refreshSurfaceWeightArray() {
-		if( terrain.surfaceArray.surfaceCount > 0 && (surfaceWeights.length != terrain.surfaceArray.surfaceCount || surfaceWeights[0].width != terrain.weightMapResolution) ) {
+		if( terrain.surfaceArray.surfaceCount > 0 && (surfaceWeights.length != terrain.surfaceArray.surfaceCount || surfaceWeights[0].width != terrain.weightMapResolution.x || surfaceWeights[0].height != terrain.weightMapResolution.y) ) {
 				var oldArray = surfaceWeights;
 				surfaceWeights = new Array<h3d.mat.Texture>();
 				surfaceWeights = [for( i in 0...terrain.surfaceArray.surfaceCount ) null];
 				for( i in 0 ... surfaceWeights.length ) {
-					surfaceWeights[i] = new h3d.mat.Texture(terrain.weightMapResolution, terrain.weightMapResolution, [Target], R8);
+					surfaceWeights[i] = new h3d.mat.Texture(terrain.weightMapResolution.x, terrain.weightMapResolution.y, [Target], R8);
 					surfaceWeights[i].setName("terrainSurfaceWeight"+i);
 					surfaceWeights[i].wrap = Clamp;
 					surfaceWeights[i].preventAutoDispose();
@@ -248,9 +247,9 @@ class Tile extends h3d.scene.Mesh {
 	}
 
 	public function generateWeightTextureArray() {
-		if( surfaceWeightArray == null || surfaceWeightArray.width != terrain.weightMapResolution || surfaceWeightArray.layerCount != terrain.surfaceArray.surfaceCount  ) {
+		if( surfaceWeightArray == null || surfaceWeightArray.width != terrain.weightMapResolution.x || surfaceWeightArray.height != terrain.weightMapResolution.y || surfaceWeightArray.layerCount != terrain.surfaceArray.surfaceCount  ) {
 			if( surfaceWeightArray != null ) surfaceWeightArray.dispose();
-			surfaceWeightArray = new h3d.mat.TextureArray(terrain.weightMapResolution, terrain.weightMapResolution, terrain.surfaceArray.surfaceCount, [Target], R8);
+			surfaceWeightArray = new h3d.mat.TextureArray(terrain.weightMapResolution.x, terrain.weightMapResolution.y, terrain.surfaceArray.surfaceCount, [Target], R8);
 			surfaceWeightArray.setName("terrainSurfaceWeightArray");
 			surfaceWeightArray.wrap = Clamp;
 			surfaceWeightArray.preventAutoDispose();
@@ -259,7 +258,7 @@ class Tile extends h3d.scene.Mesh {
 			surfaceWeightArray.realloc = function() {
 				var engine = h3d.Engine.getCurrent();
 				var unpackWeight = new h3d.pass.ScreenFx(new UnpackWeight());
-				var tmpPackedWeightTexture = new h3d.mat.Texture(terrain.weightMapResolution, terrain.weightMapResolution, [Target]);
+				var tmpPackedWeightTexture = new h3d.mat.Texture(terrain.weightMapResolution.x, terrain.weightMapResolution.y, [Target]);
 				tmpPackedWeightTexture.uploadPixels(packedWeightMapPixel);
 				for( i in 0 ... surfaceWeightArray.layerCount ) {
 					engine.pushTarget(surfaceWeightArray, i);
@@ -275,6 +274,14 @@ class Tile extends h3d.scene.Mesh {
 			if( surfaceWeights[i] != null ) terrain.copyPass.apply(surfaceWeights[i], surfaceWeightArray, None, null, i);
 	}
 
+	public function computeHeight() {
+		for( p in grid.points ) {
+			p.z = getHeight(p.x / terrain.tileSize.x, p.y / terrain.tileSize.y);
+		}
+		needAlloc = true;
+		return;
+	}
+
 	public function computeEdgesHeight( flag : haxe.EnumFlags<Direction> ) {
 
 		if( heightMap == null ) return;
@@ -314,272 +321,196 @@ class Tile extends h3d.scene.Mesh {
 	}
 
 	public function computeEdgesNormals() {
-		if( grid.normals == null ) return;
-		var t0 = new h3d.col.Point(); var t1 = new h3d.col.Point(); var t2 = new h3d.col.Point();
-		var triCount = Std.int(grid.triCount() / grid.width);
-		var vertexCount = grid.width + 1;
-		var step = hxd.Math.floor(grid.normals.length / grid.width) - 1;
-		var s = hxd.Math.floor(grid.normals.length - grid.normals.length / grid.width + 2);
-		var istep = triCount * 3 - 6;
-		var i0, i1, i2 : Int = 0;
-
-		inline function computeVertexPos( tile : Tile ) {
-			t0.load(tile.grid.points[i0]); t1.load(tile.grid.points[i1]); t2.load(tile.grid.points[i2]);
-			t0.z += tile.getHeight(t0.x / terrain.tileSize, t0.y / terrain.tileSize);
-			t1.z += tile.getHeight(t1.x / terrain.tileSize, t1.y / terrain.tileSize);
-			t2.z += tile.getHeight(t2.x / terrain.tileSize, t2.y / terrain.tileSize);
-		}
-
-		inline function computeNormal() : h3d.col.Point {
-			var n1 = t1.sub(t0);
-			n1.normalize();
-			var n2 = t2.sub(t0);
-			n2.normalize();
-			return n1.cross(n2);
-		}
 
-		var adjUpTile = terrain.getTile(tileX, tileY + 1);
-		var adjUpGrid = adjUpTile != null ? adjUpTile.grid: null;
-		var adjDownTile = terrain.getTile(tileX, tileY - 1);
-		var adjDownGrid = adjDownTile != null ? adjDownTile.grid: null;
-		var adjLeftTile = terrain.getTile(tileX + 1, tileY);
-		var adjLeftGrid = adjLeftTile != null ? adjLeftTile.grid: null;
-		var adjRightTile = terrain.getTile(tileX - 1, tileY);
-		var adjRightGrid = adjRightTile != null ? adjRightTile.grid: null;
-		var adjUpRightTile = terrain.getTile(tileX - 1, tileY + 1);
-		var adjUpRightGrid = adjUpRightTile != null ? adjUpRightTile.grid: null;
-		var adjUpLeftTile = terrain.getTile(tileX + 1, tileY + 1);
-		var adjUpLeftGrid = adjUpLeftTile != null ? adjUpLeftTile.grid: null;
-		var adjDownLeftTile = terrain.getTile(tileX + 1, tileY - 1);
-		var adjDownLeftGrid = adjDownLeftTile != null ? adjDownLeftTile.grid: null;
-		var adjDownRightTile = terrain.getTile(tileX - 1, tileY - 1);
-		var adjDownRightGrid = adjDownRightTile != null ? adjDownRightTile.grid: null;
+		if( grid.normals == null ) 
+			return;
 
+
+		inline function isEdgeIndex( grid : Grid, i : Int, side : Int ) : Bool {
+			var v = grid.points[grid.idx[i]];
+			return switch( side ) {
+				case 0: v.x == grid.width; // Left
+				case 1: v.y == grid.height; // Up
+				case 2:	v.y == 0; // Down
+				case 3: v.x == 0; // Right
+				default: false;
+			}
+		}
+		// Need to recompute the normal before any blend
+		inline function computeNormal( grid : Grid, index : Int,  assignOnSide : Int = -1 ) : h3d.col.Point {
+			var n1 = grid.points[grid.idx[index+1]].sub(grid.points[grid.idx[index]]).normalize();
+			var n2 = grid.points[grid.idx[index+2]].sub(grid.points[grid.idx[index]]).normalize();
+			var n = n1.cross(n2).normalize();
+			if( isEdgeIndex(grid, index, assignOnSide) ) grid.normals[grid.idx[index]] = grid.normals[grid.idx[index]].add(n);
+			if( isEdgeIndex(grid, index+1, assignOnSide) ) grid.normals[grid.idx[index+1]] = grid.normals[grid.idx[index+1]].add(n);
+			if( isEdgeIndex(grid, index+2, assignOnSide) ) grid.normals[grid.idx[index+2]] = grid.normals[grid.idx[index+2]].add(n);
+			return n;
+		}
+		
+		var widthVertexCount = grid.width + 1;
+		var heightVertexCount = grid.height + 1;
+		var widthTriangleCount = grid.width * 2;
+		var heightTriangleCount = grid.height * 2;
+		
+		var adjUpTile = terrain.getTile(tileX, tileY + 1);
+		var adjUpGrid = adjUpTile != null ? adjUpTile.grid : null;
 		if( adjUpGrid != null && adjUpGrid.normals != null ) {
-			var pos = 0;
-			for( i in 0 ... vertexCount )
+			for( i in 0 ... widthVertexCount ) 
 				adjUpGrid.normals[i].set(0,0,0);
-			for( i in 0 ... triCount ) {
-				i0 = adjUpGrid.idx[pos++]; i1 = adjUpGrid.idx[pos++]; i2 = adjUpGrid.idx[pos++];
-				computeVertexPos(adjUpTile);
-				var n = computeNormal();
-				if( i0 <= adjUpGrid.width ) { adjUpGrid.normals[i0].x += n.x; adjUpGrid.normals[i0].y += n.y; adjUpGrid.normals[i0].z += n.z;}
-				if( i1 <= adjUpGrid.width ) { adjUpGrid.normals[i1].x += n.x; adjUpGrid.normals[i1].y += n.y; adjUpGrid.normals[i1].z += n.z;}
-				if( i2 <= adjUpGrid.width ) { adjUpGrid.normals[i2].x += n.x; adjUpGrid.normals[i2].y += n.y; adjUpGrid.normals[i2].z += n.z;}
-			}
-			for( i in 0 ... vertexCount )
+			for( i in 0 ... widthTriangleCount ) 
+				computeNormal(adjUpGrid, i * 3, 2);
+			for( i in 0 ... widthVertexCount ) {
 				adjUpGrid.normals[i].normalize();
-			for( i in 1 ... vertexCount - 1 ) {
-				var n = grid.normals[s + i].add(adjUpGrid.normals[i]);
-				n.normalize();
-				grid.normals[s + i].load(n);
+				var n = grid.normals[i + widthVertexCount * (heightVertexCount - 1)].add(adjUpGrid.normals[i]).normalize();
+				grid.normals[i + widthVertexCount * (heightVertexCount - 1)].load(n);
 				adjUpGrid.normals[i].load(n);
 			}
+			adjUpTile.needAlloc = true;
 		}
 
+		var adjDownTile = terrain.getTile(tileX, tileY - 1);
+		var adjDownGrid = adjDownTile != null ? adjDownTile.grid : null;
 		if( adjDownGrid != null && adjDownGrid.normals != null ) {
-			var pos = triCount * (adjDownGrid.width - 1) * 3;
-			for( i in 0 ... vertexCount)
-				adjDownGrid.normals[s + i].set(0,0,0);
-			for( i in 0 ... triCount ) {
-				i0 = adjDownGrid.idx[pos++]; i1 = adjDownGrid.idx[pos++]; i2 = adjDownGrid.idx[pos++];
-				computeVertexPos(adjDownTile);
-				var n = computeNormal();
-				if( i0 >= (adjDownGrid.width * adjDownGrid.height + adjDownGrid.height) ) { adjDownGrid.normals[i0].x += n.x; adjDownGrid.normals[i0].y += n.y; adjDownGrid.normals[i0].z += n.z;}
-				if( i1 >= (adjDownGrid.width * adjDownGrid.height + adjDownGrid.height) ) { adjDownGrid.normals[i1].x += n.x; adjDownGrid.normals[i1].y += n.y; adjDownGrid.normals[i1].z += n.z;}
-				if( i2 >= (adjDownGrid.width * adjDownGrid.height + adjDownGrid.height) ) { adjDownGrid.normals[i2].x += n.x; adjDownGrid.normals[i2].y += n.y; adjDownGrid.normals[i2].z += n.z;}
-			}
-			for( i in 1 ... vertexCount - 1 )
-				adjDownGrid.normals[s + i].normalize();
-			for( i in 1 ... vertexCount - 1 ){
-				var n = grid.normals[i].add(adjDownGrid.normals[s + i]);
-				n.normalize();
+			for( i in 0 ... widthVertexCount ) 
+				adjDownGrid.normals[i + widthVertexCount * (heightVertexCount - 1)].set(0,0,0);
+			for( i in 0 ... widthTriangleCount ) 
+				computeNormal(adjDownGrid, i * 3 + widthTriangleCount * 3 * (grid.height - 1), 1);
+			for( i in 0 ... widthVertexCount ) {
+				adjDownGrid.normals[i + widthVertexCount * (heightVertexCount - 1)].normalize();
+				var n = grid.normals[i].add(adjDownGrid.normals[i + widthVertexCount * (heightVertexCount - 1)]).normalize();
 				grid.normals[i].load(n);
-				adjDownGrid.normals[s + i].load(n);
+				adjDownGrid.normals[i + widthVertexCount * (heightVertexCount - 1)].load(n);
 			}
+			adjDownTile.needAlloc = true;
 		}
 
+		var adjLeftTile = terrain.getTile(tileX + 1, tileY);
+		var adjLeftGrid = adjLeftTile != null ? adjLeftTile.grid : null;
 		if( adjLeftGrid != null && adjLeftGrid.normals != null ) {
-			var pos = 0;
-			var istep = triCount * 3 - 6;
-			var needStep = false;
-			for( i in 0 ... vertexCount )
-				adjLeftGrid.normals[i * step].set(0,0,0);
-			for( i in 0 ... triCount ) {
-				i0 = adjLeftGrid.idx[pos++]; i1 = adjLeftGrid.idx[pos++]; i2 = adjLeftGrid.idx[pos++];
-				computeVertexPos(adjLeftTile);
-				var n = computeNormal();
-				if( i0 % (adjLeftGrid.width + 1) == 0 ) { adjLeftGrid.normals[i0].x += n.x; adjLeftGrid.normals[i0].y += n.y; adjLeftGrid.normals[i0].z += n.z;}
-				if( i1 % (adjLeftGrid.width + 1) == 0 ) { adjLeftGrid.normals[i1].x += n.x; adjLeftGrid.normals[i1].y += n.y; adjLeftGrid.normals[i1].z += n.z;}
-				if( i2 % (adjLeftGrid.width + 1) == 0 ) { adjLeftGrid.normals[i2].x += n.x; adjLeftGrid.normals[i2].y += n.y; adjLeftGrid.normals[i2].z += n.z;}
-				if( needStep) pos += istep;
-				needStep = !needStep;
+			for( i in 0 ... heightVertexCount ) 
+				adjLeftGrid.normals[i * widthVertexCount].set(0,0,0);
+			for( i in 0 ... grid.height ) {
+				computeNormal(adjLeftGrid, i * widthTriangleCount * 3, 0);
+				computeNormal(adjLeftGrid, i * widthTriangleCount * 3 + 3, 3);
 			}
-			for( i in 0 ... vertexCount )
-				adjLeftGrid.normals[i * step].normalize();
-			for( i in 1 ... vertexCount - 1 ){
-				var n = grid.normals[i * step + (step - 1)].add(adjLeftGrid.normals[i * step]);
-				n.normalize();
-				grid.normals[i * step + (step - 1)].load(n);
-				adjLeftGrid.normals[i * step].load(n);
+			for( i in 0 ... heightVertexCount ) {
+				adjLeftGrid.normals[i * widthVertexCount].normalize();
+				var n = grid.normals[(widthVertexCount - 1) + i * widthVertexCount].add(adjLeftGrid.normals[i * widthVertexCount]).normalize();
+				grid.normals[(widthVertexCount - 1) + i * widthVertexCount].load(n);
+				adjLeftGrid.normals[i * widthVertexCount].load(n);
 			}
+			adjLeftTile.needAlloc = true;
 		}
 
+		var adjRightTile = terrain.getTile(tileX - 1, tileY);
+		var adjRightGrid = adjRightTile != null ? adjRightTile.grid : null;
 		if( adjRightGrid != null && adjRightGrid.normals != null ) {
-			var pos = (triCount - 2) * 3;
-			var istep = (triCount - 2) * 3;
-			var needStep = false;
-			for( i in 0 ... vertexCount )
-				adjRightGrid.normals[i * step + (step - 1)].set(0,0,0);
-			for( i in 0 ... triCount ) {
-				i0 = adjRightGrid.idx[pos++]; i1 = adjRightGrid.idx[pos++]; i2 = adjRightGrid.idx[pos++];
-				computeVertexPos(adjRightTile);
-				var n = computeNormal();
-				if( (i0 + 1) % (adjRightGrid.width + 1) == 0 ) { adjRightGrid.normals[i0].x += n.x; adjRightGrid.normals[i0].y += n.y; adjRightGrid.normals[i0].z += n.z; }
-				if( (i1 + 1) % (adjRightGrid.width + 1) == 0 ) { adjRightGrid.normals[i1].x += n.x; adjRightGrid.normals[i1].y += n.y; adjRightGrid.normals[i1].z += n.z; }
-				if( (i2 + 1) % (adjRightGrid.width + 1) == 0 ) { adjRightGrid.normals[i2].x += n.x; adjRightGrid.normals[i2].y += n.y; adjRightGrid.normals[i2].z += n.z; }
-				if( needStep) pos += istep;
-				needStep = !needStep;
+			for( i in 0 ... heightVertexCount ) 
+				adjRightGrid.normals[(widthVertexCount - 1) + i * widthVertexCount].set(0,0,0);
+			for( i in 0 ... grid.height ) {
+				computeNormal(adjRightGrid, (widthTriangleCount - 1) * 3 + i * widthTriangleCount * 3, 0);
+				computeNormal(adjRightGrid, (widthTriangleCount - 2) * 3 + i * widthTriangleCount * 3, 0);
 			}
-			for( i in 0 ... vertexCount )
-				adjRightGrid.normals[i * step + (step - 1)].normalize();
-			for( i in 1 ... vertexCount - 1 ) {
-				var n = grid.normals[i * step].add(adjRightGrid.normals[i * step + (step - 1)]);
-				n.normalize();
-				grid.normals[i * step].load(n);
-				adjRightGrid.normals[i * step + (step - 1)].load(n);
+			for( i in 0 ... heightVertexCount ) {
+				adjRightGrid.normals[(widthVertexCount - 1) + i * widthVertexCount].normalize();
+				var n = grid.normals[i * widthVertexCount].add(adjRightGrid.normals[(widthVertexCount - 1) + i * widthVertexCount]).normalize();
+				grid.normals[i * widthVertexCount].load(n);
+				adjRightGrid.normals[(widthVertexCount - 1) + i * widthVertexCount].load(n);
 			}
+			adjRightTile.needAlloc = true;
 		}
+		
+		var adjUpRightTile = terrain.getTile(tileX - 1, tileY + 1);
+		var adjUpRightGrid = adjUpRightTile != null ? adjUpRightTile.grid : null;
+		var adjUpLeftTile = terrain.getTile(tileX + 1, tileY + 1);
+		var adjUpLeftGrid = adjUpLeftTile != null ? adjUpLeftTile.grid : null;
+		var adjDownLeftTile = terrain.getTile(tileX + 1, tileY - 1);
+		var adjDownLeftGrid = adjDownLeftTile != null ? adjDownLeftTile.grid : null;
+		var adjDownRightTile = terrain.getTile(tileX - 1, tileY - 1);
+		var adjDownRightGrid = adjDownRightTile != null ? adjDownRightTile.grid : null;
 
-		var topLeft = grid.points.length - 1;
-		var downLeft= step - 1;
+		var upLeft = grid.points.length - 1;
 		var downRight = 0;
-		var upRight = step * grid.height;
-
+		var downLeft = grid.width;
+		var upRight = (grid.width + 1) * (grid.height);
 		var n = new h3d.col.Point();
-		if( adjUpRightGrid != null && adjUpRightGrid.normals != null ) {
-			var pos = (triCount) * 3 - 6;
-			i0 = adjUpRightGrid.idx[pos++]; i1 = adjUpRightGrid.idx[pos++]; i2 = adjUpRightGrid.idx[pos++];
-			computeVertexPos(adjUpRightTile);
-			n = computeNormal();
-			i0 = adjUpRightGrid.idx[pos++]; i1 = adjUpRightGrid.idx[pos++]; i2 = adjUpRightGrid.idx[pos++];
-			computeVertexPos(adjUpRightTile);
-			n = n.add(computeNormal());
-			n.normalize();
-		}
-		if( adjRightGrid != null && adjRightGrid.normals != null ) n = n.add(adjRightGrid.normals[topLeft]);
-		if( adjUpGrid != null && adjUpGrid.normals != null ) n = n.add(adjUpGrid.normals[downRight]);
-		n = n.add(grid.normals[upRight]);
+
+		inline function computeUpLeftNormal( grid : Grid ) : h3d.col.Point {
+			return grid == null ? new h3d.col.Point() : computeNormal(grid, ((widthTriangleCount - 1) + (grid.height - 1) * widthTriangleCount) * 3);
+		}
+		inline function computeDownLeftNormal( grid : Grid ) : h3d.col.Point {
+			return grid == null ? new h3d.col.Point() : computeNormal(grid, (heightTriangleCount - 1) * 3).add(computeNormal(grid, (heightTriangleCount - 2) * 3));
+		}
+		inline function computeUpRightNormal( grid : Grid ) : h3d.col.Point {
+			return grid == null ? new h3d.col.Point() : computeNormal(grid, (grid.height - 1) * widthTriangleCount * 3).add(computeNormal(grid, (grid.height - 1) * widthTriangleCount * 3 + 3));
+		}
+		inline function computeDownRightNormal( grid : Grid ) : h3d.col.Point {
+			return grid == null ? new h3d.col.Point() : computeNormal(grid, 0);
+		}
+		
+		// Up Right Corner
+		n.set(0,0,0);
+		n = n.add(computeDownLeftNormal(adjUpRightGrid));
+		n = n.add(computeUpLeftNormal(adjRightGrid));
+		n = n.add(computeDownRightNormal(adjUpGrid));
+		n = n.add(computeUpRightNormal(grid));
 		n.normalize();
-		if( adjUpRightGrid != null && adjUpRightGrid.normals != null ) adjUpRightGrid.normals[downLeft].load(n);
-		if( adjRightGrid != null && adjRightGrid.normals != null ) adjRightGrid.normals[topLeft].load(n);
-		if( adjUpGrid != null && adjUpGrid.normals != null ) adjUpGrid.normals[downRight].load(n);
+		if(	adjUpRightGrid != null ) adjUpRightGrid.normals[downLeft].load(n);
+		if( adjRightGrid != null ) adjRightGrid.normals[upLeft].load(n);
+		if( adjUpGrid != null ) adjUpGrid.normals[downRight].load(n);
 		grid.normals[upRight].load(n);
+		if( adjUpRightTile != null ) adjUpRightTile.needAlloc = true;
 
+		// Up Left Corner
 		n.set(0,0,0);
-		if( adjUpLeftGrid != null && adjUpLeftGrid.normals != null ) {
-			var pos = 0;
-			i0 = adjUpLeftGrid.idx[pos++]; i1 = adjUpLeftGrid.idx[pos++]; i2 = adjUpLeftGrid.idx[pos++];
-			computeVertexPos(adjUpLeftTile);
-			n = computeNormal();
-			n.normalize();
-		}
-		if( adjLeftGrid != null && adjLeftGrid.normals != null ) n = n.add(adjLeftGrid.normals[upRight]);
-		if( adjUpGrid != null && adjUpGrid.normals != null ) n = n.add(adjUpGrid.normals[downLeft]);
-		n = n.add(grid.normals[topLeft]);
+		n = n.add(computeDownLeftNormal(adjUpGrid));
+		n = n.add(computeUpLeftNormal(grid));
+		n = n.add(computeDownRightNormal(adjUpLeftGrid));
+		n = n.add(computeUpRightNormal(adjLeftGrid));
 		n.normalize();
-		if( adjUpLeftGrid != null && adjUpLeftGrid.normals != null ) adjUpLeftGrid.normals[downRight].load(n);
-		if( adjLeftGrid != null && adjLeftGrid.normals != null ) adjLeftGrid.normals[upRight].load(n);
-		if( adjUpGrid != null && adjUpGrid.normals != null ) adjUpGrid.normals[downLeft].load(n);
-		grid.normals[topLeft].load(n);
+		if( adjUpLeftGrid != null ) adjUpLeftGrid.normals[downRight].load(n);
+		if( adjLeftGrid != null ) adjLeftGrid.normals[upRight].load(n);
+		if( adjUpGrid != null ) adjUpGrid.normals[downLeft].load(n);
+		grid.normals[upLeft].load(n);
+		if( adjUpLeftTile != null ) adjUpLeftTile.needAlloc = true;
 
+		// Down Left Corner
 		n.set(0,0,0);
-		if( adjDownLeftGrid != null && adjDownLeftGrid.normals != null ) {
-			var pos = (triCount) * 3 * (adjDownLeftGrid.height - 1) ;
-			i0 = adjDownLeftGrid.idx[pos++]; i1 = adjDownLeftGrid.idx[pos++]; i2 = adjDownLeftGrid.idx[pos++];
-			computeVertexPos(adjDownLeftTile);
-			n = computeNormal();
-			i0 = adjDownLeftGrid.idx[pos++]; i1 = adjDownLeftGrid.idx[pos++]; i2 = adjDownLeftGrid.idx[pos++];
-			computeVertexPos(adjDownLeftTile);
-			n = n.add(computeNormal());
-			n.normalize();
-		}
-		if( adjLeftGrid != null && adjLeftGrid.normals != null ) n = n.add(adjLeftGrid.normals[downRight]);
-		if( adjDownGrid != null && adjDownGrid.normals != null ) n = n.add(adjDownGrid.normals[topLeft]);
-		n = n.add(grid.normals[downLeft]);
+		n = n.add(computeDownLeftNormal(grid));
+		n = n.add(computeUpLeftNormal(adjDownGrid));
+		n = n.add(computeDownRightNormal(adjLeftGrid));
+		n = n.add(computeUpRightNormal(adjDownLeftGrid));
 		n.normalize();
-		if( adjDownLeftGrid != null && adjDownLeftGrid.normals != null ) adjDownLeftGrid.normals[upRight].load(n);
-		if( adjLeftGrid != null && adjLeftGrid.normals != null ) adjLeftGrid.normals[downRight].load(n);
-		if( adjDownGrid != null && adjDownGrid.normals != null ) adjDownGrid.normals[topLeft].load(n);
+		if( adjDownLeftGrid != null ) adjDownLeftGrid.normals[upRight].load(n);
+		if( adjLeftGrid != null ) adjLeftGrid.normals[downRight].load(n);
+		if( adjDownGrid != null ) adjDownGrid.normals[upLeft].load(n);
 		grid.normals[downLeft].load(n);
+		if( adjDownLeftTile != null ) adjDownLeftTile.needAlloc = true;
 
+		// Down Right Corner
 		n.set(0,0,0);
-		if( adjDownRightGrid != null && adjDownRightGrid.normals != null ) {
-			var pos = triCount * 3 * adjDownRightGrid.width - 3;
-			i0 = adjDownRightGrid.idx[pos++]; i1 = adjDownRightGrid.idx[pos++]; i2 = adjDownRightGrid.idx[pos++];
-			computeVertexPos(adjDownRightTile);
-			n = computeNormal();
-			n.normalize();
-		}
-		if( adjRightGrid != null && adjRightGrid.normals != null ) n = n.add(adjRightGrid.normals[downLeft]);
-		if( adjDownGrid != null && adjDownGrid.normals != null ) n = n.add(adjDownGrid.normals[upRight]);
-		n = n.add(grid.normals[downRight]);
+		n = n.add(computeDownLeftNormal(adjRightGrid));
+		n = n.add(computeUpLeftNormal(adjDownRightGrid));
+		n = n.add(computeDownRightNormal(grid));
+		n = n.add(computeUpRightNormal(adjDownGrid));
 		n.normalize();
-		if( adjDownRightGrid != null && adjDownRightGrid.normals != null ) adjDownRightGrid.normals[topLeft].load(n);
-		if( adjRightGrid != null && adjRightGrid.normals != null ) adjRightGrid.normals[downLeft].load(n);
-		if( adjDownGrid != null && adjDownGrid.normals != null ) adjDownGrid.normals[upRight].load(n);
+		if( adjDownRightGrid != null ) adjDownRightGrid.normals[upLeft].load(n);
+		if( adjRightGrid != null ) adjRightGrid.normals[downLeft].load(n);
+		if( adjDownGrid != null ) adjDownGrid.normals[upRight].load(n);
 		grid.normals[downRight].load(n);
-
-		if( adjUpTile != null ) adjUpTile.needAlloc = true;
-		if( adjDownTile != null ) adjDownTile.needAlloc = true;
-		if( adjLeftTile != null ) adjLeftTile.needAlloc = true;
-		if( adjRightTile != null ) adjRightTile.needAlloc = true;
-		if( adjUpLeftTile != null ) adjUpLeftTile.needAlloc = true;
 		if( adjDownLeftTile != null ) adjDownLeftTile.needAlloc = true;
-		if( adjUpRightTile != null ) adjUpRightTile.needAlloc = true;
-		if( adjDownRightTile != null ) adjDownRightTile.needAlloc = true;
-		this.needAlloc = true;
+		
+		needAlloc = true;
 	}
 
 	public function computeNormals() {
-		if( grid.normals == null ) grid.normals = new Array<h3d.col.Point>();
-		grid.normals = [
-		for ( i in 0...grid.points.length ) {
-			if( i < grid.normals.length ) {
-				grid.normals[i].set(0,0,0);
-				grid.normals[i];
-			} else
-				new h3d.col.Point();
-		}];
-
-		var t0 = new h3d.col.Point(); var t1 = new h3d.col.Point(); var t2 = new h3d.col.Point();
-		var pos = 0;
-		for( i in 0...grid.triCount() ) {
-			var i0, i1, i2;
-			if( grid.idx == null ) {
-				i0 = pos++; i1 = pos++; i2 = pos++;
-			} else {
-				i0 = grid.idx[pos++]; i1 = grid.idx[pos++]; i2 = grid.idx[pos++];
-			}
-			t0.load(grid.points[i0]); t1.load(grid.points[i1]); t2.load(grid.points[i2]);
-			if(heightMap != null){
-				t0.z += getHeight(t0.x / terrain.tileSize, t0.y / terrain.tileSize);
-				t1.z += getHeight(t1.x / terrain.tileSize, t1.y / terrain.tileSize);
-				t2.z += getHeight(t2.x / terrain.tileSize, t2.y / terrain.tileSize);
-			}
-			var n1 = t1.sub(t0);
-			n1.normalizeFast();
-			var n2 = t2.sub(t0);
-			n2.normalizeFast();
-			var n = n1.cross(n2);
-			grid.normals[i0].x += n.x; grid.normals[i0].y += n.y; grid.normals[i0].z += n.z;
-			grid.normals[i1].x += n.x; grid.normals[i1].y += n.y; grid.normals[i1].z += n.z;
-			grid.normals[i2].x += n.x; grid.normals[i2].y += n.y; grid.normals[i2].z += n.z;
-		}
-		for( n in grid.normals )
-			n.normalize();
+		if( grid != null )
+			grid.addNormals();
+	}
 
-		needAlloc = true;
+	public function computeTangents() {
+		if( grid != null )
+			grid.addTangents();
 	}
 
 	public function getHeight( u : Float, v : Float, ?fast = false ) : Float {
@@ -590,7 +521,7 @@ class Tile extends h3d.scene.Mesh {
 				return pixels.getPixelF(Std.int(hxd.Math.clamp(u, 0, pixels.width - 1)), Std.int(hxd.Math.clamp(v, 0, pixels.height - 1))).r;
 			}
 			var px = u * (pixels.width - 1) ;
-            var py = v * (pixels.width - 1) ;
+            var py = v * (pixels.height - 1) ;
 			var pxi = hxd.Math.floor(px);
             var pyi = hxd.Math.floor(py);
 			var c00 = getPix(pxi, pyi);
@@ -616,9 +547,9 @@ class Tile extends h3d.scene.Mesh {
 		if( cachedBounds == null ) {
 			if( heightMap != null ) {
 				cachedBounds = new h3d.col.Bounds();
-				cachedBounds.xMax = terrain.tileSize;
+				cachedBounds.xMax = terrain.tileSize.x;
 				cachedBounds.xMin = 0.0;
-				cachedBounds.yMax = terrain.tileSize;
+				cachedBounds.yMax = terrain.tileSize.y;
 				cachedBounds.yMin = 0.0;
 				for( u in 0 ... heightMap.width ) {
 					for( v in 0 ... heightMap.height ) {
@@ -654,9 +585,8 @@ class Tile extends h3d.scene.Mesh {
 		shader.SURFACE_COUNT = terrain.surfaceArray.surfaceCount;
 		shader.PARALLAX = terrain.enableParallax && terrain.parallaxAmount != 0;
 
-		shader.heightMapSize = heightMap != null ? heightMap.width : 0;
-		shader.primSize = terrain.tileSize;
-		shader.cellSize = terrain.cellSize;
+		shader.primSize.set(terrain.tileSize.x, terrain.tileSize.y);
+		shader.cellSize.set(terrain.cellSize.x, terrain.cellSize.y);
 
 		shader.albedoTextures = terrain.surfaceArray.albedo;
 		shader.normalTextures = terrain.surfaceArray.normal;

+ 1 - 1
hrt/shader/Brush.hx

@@ -6,7 +6,7 @@ class Brush extends hxsl.Shader {
 
 		@:import h3d.shader.Base2d;
 		@param var strength : Float;
-		@param var size : Float;
+		@param var size : Vec2;
 		@param var pos : Vec3;
 
 		function fragment() {

+ 32 - 40
hrt/shader/Terrain.hx

@@ -13,9 +13,8 @@ class Terrain extends hxsl.Shader {
 		@const var PARALLAX : Bool;
 		@const var VERTEX_DISPLACEMENT : Bool;
 
-		@param var heightMapSize : Float;
-		@param var primSize : Float;
-		@param var cellSize : Float;
+		@param var primSize : Vec2;
+		@param var cellSize : Vec2;
 
 		@param var albedoTextures : Sampler2DArray;
 		@param var normalTextures : Sampler2DArray;
@@ -39,8 +38,8 @@ class Terrain extends hxsl.Shader {
 		@param var maxStep : Int;
 		@param var tileIndex : Vec2;
 
+		var worldUV : Vec2;
 		var calculatedUV : Vec2;
-		var terrainUV : Vec2;
 		var TBN : Mat3;
 
 		var emissiveValue : Float;
@@ -54,12 +53,7 @@ class Terrain extends hxsl.Shader {
 
 		function vertex() {
 			calculatedUV = input.position.xy / primSize;
-			if(VERTEX_DISPLACEMENT) {
-				var terrainUV = (calculatedUV * (heightMapSize - 1)) / heightMapSize;
-				terrainUV += 0.5 / heightMapSize;
-				transformedPosition += (vec3(0,0, textureLod(heightMap, terrainUV, 0).r) * global.modelView.mat3());
-			}
-
+			worldUV = transformedPosition.xy;
 			transformedTangent = vec4(tangent * global.modelView.mat3(),tangent.dot(tangent) > 0.5 ? 1. : -1.);
 			var tanX = transformedTangent.xyz.normalize() * -transformedTangent.w;
 			var tanY = transformedNormal.cross(tanX).normalize();
@@ -87,40 +81,38 @@ class Terrain extends hxsl.Shader {
 		var w : Vec3;
 		var i : IVec3;
 		function getPOMUV( i : IVec3, uv : Vec2 ) : Vec2 {
-			if( !PARALLAX )
-				return uv;
-			else {
-				var viewNS = normalize(tangentViewPos - tangentFragPos);
-				var numLayers = mix(float(maxStep), float(minStep), viewNS.dot(transformedNormal));
-				var layerDepth = 1 / numLayers;
-				var curLayerDepth = 0.;
-				var delta = (viewNS.xy / viewNS.z) * parallaxAmount / numLayers;
-				var curUV = uv;
-				var depth = getDepth(i, curUV);
-				var curDepth = depth.dot(w);
-				var prevDepth = 0.;
-				while( curLayerDepth < curDepth ) {
-					curUV += delta;
-					prevDepth = curDepth;
-					i = ivec3(surfaceIndexMap.getLod(curUV, 0).rgb * 255);
-					w = getWeight(i, curUV);
-					depth = getDepth(i, curUV);
-					curDepth = depth.dot(w);
-					curLayerDepth += layerDepth;
-				}
-				var prevUV = curUV - delta;
-				var after = curDepth - curLayerDepth;
-				var before = prevDepth - curLayerDepth + layerDepth;
-				var pomUV = mix(curUV, prevUV,  after / (after - before));
-				return pomUV;
+			var viewNS = normalize(tangentViewPos - tangentFragPos);
+			var numLayers = mix(float(maxStep), float(minStep), viewNS.dot(transformedNormal));
+			var layerDepth = 1 / numLayers;
+			var curLayerDepth = 0.;
+			var delta = (viewNS.xy / viewNS.z) * parallaxAmount / numLayers;
+			var curUV = uv;
+			var depth = getDepth(i, curUV);
+			var curDepth = depth.dot(w);
+			var prevDepth = 0.;
+			while( curLayerDepth < curDepth ) {
+				curUV += delta;
+				prevDepth = curDepth;
+				i = ivec3(surfaceIndexMap.getLod(curUV, 0).rgb * 255);
+				w = getWeight(i, curUV);
+				depth = getDepth(i, curUV);
+				curDepth = depth.dot(w);
+				curLayerDepth += layerDepth;
 			}
+			var prevUV = curUV - delta;
+			var after = curDepth - curLayerDepth;
+			var before = prevDepth - curLayerDepth + layerDepth;
+			var pomUV = mix(curUV, prevUV,  after / (after - before));
+			return pomUV;
+
 		}
 
 		function getsurfaceUV( id : Int, uv : Vec2 ) : Vec3 {
+			uv = transformedPosition.xy + (input.position.xy - uv * primSize); // Local To world
 			var angle = surfaceParams[id].w;
 			var offset = vec2(surfaceParams[id].y, surfaceParams[id].z);
 			var tilling = surfaceParams[id].x;
-			var worldUV = (uv + tileIndex) * tilling + offset;
+			var worldUV = uv * tilling + offset;
 			var res = vec2( worldUV.x * cos(angle) - worldUV.y * sin(angle) , worldUV.y * cos(angle) + worldUV.x * sin(angle));
 			var surfaceUV = vec3(res % 1, id);
 			return surfaceUV;
@@ -159,7 +151,7 @@ class Terrain extends hxsl.Shader {
 			else {
 				i = ivec3(surfaceIndexMap.get(calculatedUV).rgb * 255);
 				w = getWeight(i, calculatedUV);
-				var pomUV = getPOMUV(i, calculatedUV);
+				var pomUV = PARALLAX ? getPOMUV(i, calculatedUV) : calculatedUV;
 				if( PARALLAX ) {
 					i = ivec3(surfaceIndexMap.get(pomUV).rgb * 255);
 					w = getWeight(i, pomUV);
@@ -210,7 +202,7 @@ class Terrain extends hxsl.Shader {
 
 				// Output
 				var n = unpackNormal(normal);
-				transformedNormal = vec3(n.x * -1, n.y, n.z) * TBN;
+				//transformedNormal = vec3(n.x * -1, n.y, n.z) * TBN;
 				pixelColor = vec4(albedo, 1.0);
 				roughnessValue = 1 - pbr.g * pbr.g;
 				metalnessValue = pbr.r;
@@ -221,7 +213,7 @@ class Terrain extends hxsl.Shader {
 			if( SHOW_GRID ) {
 				var gridColor = vec4(1,0,0,1);
 				var tileEdgeColor = vec4(1,1,0,1);
-				var grid : Vec2 = ((input.position.xy.mod(cellSize) / cellSize ) - 0.5) * 2.0;
+				var grid : Vec2 = ((input.position.xy.mod(cellSize.xy) / cellSize.xy ) - 0.5) * 2.0;
 				grid = ceil(max(vec2(0), abs(grid) - 0.9));
 				var tileEdge = max( (1 - ceil(input.position.xy / primSize - 0.1 / (primSize / cellSize) )), floor(input.position.xy / primSize + 0.1 / (primSize / cellSize)));
 				emissiveValue = max(max(grid.x, grid.y), max(tileEdge.x, tileEdge.y));