public override void ProcessDetails(TerrainWrapper wrapper, LayerBase baseLayer, int stencilKey) { var layer = baseLayer as MMTerrainLayer; if (layer == null) { Debug.LogWarning(string.Format("Attempted to write {0} to incorrect layer type! Expected Layer {1} to be {2}, but it was {3}", name, baseLayer.name, GetLayerType(), baseLayer.GetType()), this); return; } if (!Network || Configuration == null) { return; } var config = Configuration.GetConfig <Config>(); if (config == null) { Debug.LogError("Invalid configuration! Expected ConnectionTerrainHeightConfiguration"); return; } var terrain = wrapper.Terrain; var dRes = terrain.terrainData.detailResolution; var mainSpline = NodeConnection.GetSpline(); var radius = config.Radius; var falloffCurve = config.GrassFalloff; // Create bounds to encapsulate spline (axis aligned) var bounds = mainSpline.GetApproximateBounds(); bounds.Expand(radius * 2 * Vector3.one); // Create object bounds var objectBounds = mainSpline.GetApproximateXZObjectBounds(); objectBounds.Expand(radius * 2 * Vector3.one); objectBounds.Expand(Vector3.up * 10000); // Early cull var axisBounds = objectBounds.ToAxisBounds(); var terrainBounds = terrain.GetComponent <Collider>().bounds; if (!terrainBounds.Intersects(axisBounds)) { return; } // Get matrix space min/max var matrixMin = terrain.WorldToDetailCoord(bounds.min); matrixMin = new Common.Coord(Mathf.Clamp(matrixMin.x, 0, dRes), Mathf.Clamp(matrixMin.z, 0, dRes)); var matrixMax = terrain.WorldToDetailCoord(bounds.max); matrixMax = new Common.Coord(Mathf.Clamp(matrixMax.x, 0, dRes), Mathf.Clamp(matrixMax.z, 0, dRes)); var floatArraySize = new Common.Coord(matrixMax.x - matrixMin.x, matrixMax.z - matrixMin.z); var layerDetails = layer.GetDetailMaps(matrixMin.x, matrixMin.z, floatArraySize.x, floatArraySize.z, dRes); var writeStencil = new Serializable2DFloatArray(floatArraySize.x, floatArraySize.z); for (var dx = 0; dx < floatArraySize.x; ++dx) { for (var dz = 0; dz < floatArraySize.z; ++dz) { var coordX = matrixMin.x + dx; var coordZ = matrixMin.z + dz; var worldPos = terrain.DetailCoordToWorldPos(new Common.Coord(coordX, coordZ)); worldPos = new Vector3(worldPos.x, objectBounds.center.y, worldPos.z); if (!terrain.ContainsPointXZ(worldPos) || !objectBounds.Contains(worldPos)) { continue; } var uniformT = mainSpline.GetClosestUniformTimeOnSplineXZ(worldPos.xz()); // Expensive! var closestOnSpline = mainSpline.GetUniformPointOnSpline(uniformT); var normalizedFlatDistToSpline = (worldPos - closestOnSpline).xz().magnitude / radius; var maskValue = Mathf.Clamp01(falloffCurve.Evaluate(normalizedFlatDistToSpline)); if (maskValue <= 0 || normalizedFlatDistToSpline < 0 || normalizedFlatDistToSpline > 1) { //DebugHelper.DrawPoint(worldPos, 1, Color.yellow, 20); continue; } //DebugHelper.DrawPoint(worldPos, .2f, Color.green, 20); //Debug.DrawLine(worldPos, worldPos + Vector3.up * maskValue, Color.green, 20); writeStencil[dx, dz] = 1; layer.Stencil[coordX, coordZ] = MiscUtilities.CompressStencil(GetPriority(), 1); foreach (var data in layerDetails) { float readValue = data.Value[dx, dz]; readValue /= 16; var writeValue = readValue * (1 - maskValue); var writeByteValue = (byte)Mathf.Clamp(writeValue * 16, 0, 16); data.Value[dx, dz] = writeByteValue; } } } foreach (var serializable2DByteArray in layerDetails) { layer.SetDetailMap(serializable2DByteArray.Key, matrixMin.x, matrixMin.z, serializable2DByteArray.Value, dRes, writeStencil); } }
public void Process(global::MapMagic.CoordRect rect, Chunk.Results results, GeneratorsAsset gens, Chunk.Size terrainSize, Func <float, bool> stop = null) { if (stop != null && stop(0)) { return; } Matrix result = new Matrix(rect); foreach (MadMapsStencilOutput gen in gens.GeneratorsOfType <MadMapsStencilOutput>(onlyEnabled: true, checkBiomes: true)) { Matrix input = (Matrix)gen.input.GetObject(results); if (input == null) { continue; } //loading biome matrix Matrix biomeMask = null; if (gen.biome != null) { object biomeMaskObj = gen.biome.mask.GetObject(results); if (biomeMaskObj == null) { continue; //adding nothing if biome has no mask } biomeMask = (Matrix)biomeMaskObj; if (biomeMask == null) { continue; } if (biomeMask.IsEmpty()) { continue; //optimizing empty biomes } } //adding to final result if (gen.biome == null) { result.Add(input); } else if (biomeMask != null) { result.Add(input, biomeMask); } } //creating 2d array if (stop != null && stop(0)) { return; } int heightSize = terrainSize.resolution; var stencil = new Stencil(heightSize, heightSize); int key = 1; for (int x = 0; x < heightSize - 1; x++) { for (int z = 0; z < heightSize - 1; z++) { float strength; int disposableKey; MiscUtilities.DecompressStencil(stencil[x, z], out disposableKey, out strength); var writeValue = result[x + results.heights.rect.offset.x, z + results.heights.rect.offset.z]; stencil[x, z] = MiscUtilities.CompressStencil(key, strength + writeValue); } } //pushing to apply if (stop != null && stop(0)) { return; } results.apply.CheckAdd(typeof(MadMapsStencilOutput), stencil, replace: true); }
public override void ProcessSplats(TerrainWrapper wrapper, LayerBase baseLayer, int stencilKey) { var layer = baseLayer as MMTerrainLayer; if (layer == null) { Debug.LogWarning(string.Format("Attempted to write {0} to incorrect layer type! Expected Layer {1} to be {2}, but it was {3}", name, baseLayer.name, GetLayerType(), baseLayer.GetType()), this); return; } if (!Network || Configuration == null) { return; } var config = Configuration.GetConfig <Config>(); if (config == null) { Debug.LogError("Invalid configuration! Expected ConnectionTerrainHeightConfiguration"); return; } var terrain = wrapper.Terrain; var splatRes = wrapper.Terrain.terrainData.alphamapResolution; var mainSpline = NodeConnection.GetSpline(); var radius = config.Radius; // Create bounds to encapsulate spline (axis aligned) var bounds = mainSpline.GetApproximateBounds(); bounds.Expand(radius * 2 * Vector3.one); // Create object bounds var objectBounds = mainSpline.GetApproximateXZObjectBounds(); objectBounds.Expand(radius * 2 * Vector3.one); objectBounds.Expand(Vector3.up * 10000); // Early cull var axisBounds = objectBounds.ToAxisBounds(); var terrainBounds = wrapper.Terrain.GetComponent <Collider>().bounds; if (!terrainBounds.Intersects(axisBounds)) { return; } float planeGive = -(wrapper.Terrain.terrainData.size.x / wrapper.Terrain.terrainData.alphamapResolution) * SplatOffset; Plane startPlane, endPlane; GenerateSplinePlanes(planeGive, mainSpline, out startPlane, out endPlane); // Get matrix space min/max var matrixMin = terrain.WorldToSplatCoord(bounds.min, TerrainX.RoundType.Floor); var matrixMax = terrain.WorldToSplatCoord(bounds.max, TerrainX.RoundType.Ceil); matrixMin = new Common.Coord(Mathf.Clamp(matrixMin.x, 0, terrain.terrainData.alphamapResolution), Mathf.Clamp(matrixMin.z, 0, terrain.terrainData.alphamapResolution)); matrixMax = new Common.Coord(Mathf.Clamp(matrixMax.x, 0, terrain.terrainData.alphamapResolution), Mathf.Clamp(matrixMax.z, 0, terrain.terrainData.alphamapResolution)); var floatArraySize = new Common.Coord(matrixMax.x - matrixMin.x, matrixMax.z - matrixMin.z); // Get all the existing compound splats var currentPrototypes = wrapper.GetCompoundSplatPrototypes(layer, true); var baseData = layer.GetSplatMaps(matrixMin.x, matrixMin.z, floatArraySize.x, floatArraySize.z, splatRes); stencilKey = GetStencilKey(); Serializable2DFloatArray thisPatchStencil = new Serializable2DFloatArray(floatArraySize.x, floatArraySize.z); foreach (var splatConfiguration in config.SplatConfigurations) { var splatPrototypeWrapper = splatConfiguration.SplatPrototype; Serializable2DByteArray baseLayerSplat; if (!baseData.TryGetValue(splatPrototypeWrapper, out baseLayerSplat)) { baseLayerSplat = new Serializable2DByteArray(floatArraySize.x, floatArraySize.z); baseData[splatPrototypeWrapper] = baseLayerSplat; } for (var dz = 0; dz < floatArraySize.z; ++dz) { for (var dx = 0; dx < floatArraySize.x; ++dx) { var coordX = matrixMin.x + dx; var coordZ = matrixMin.z + dz; var worldPos = terrain.SplatCoordToWorldPos(new Common.Coord(coordX, coordZ)); worldPos = new Vector3(worldPos.x, objectBounds.center.y, worldPos.z); if (terrain.ContainsPointXZ(worldPos) && objectBounds.Contains(worldPos) && GeometryExtensions.BetweenPlanes(worldPos, startPlane, endPlane)) { var uniformT = mainSpline.GetClosestUniformTimeOnSplineXZ(worldPos.xz()); // Expensive! var closestOnSpline = mainSpline.GetUniformPointOnSpline(uniformT); var normalizedFlatDistToSpline = (worldPos - closestOnSpline).xz().magnitude / (config.Radius); if (normalizedFlatDistToSpline != Mathf.Clamp01(normalizedFlatDistToSpline)) { continue; } var maskValue = config.SplatFalloff.Evaluate(normalizedFlatDistToSpline); var writeFloatValue = splatConfiguration.SplatStrength * maskValue; var writeValue = (byte)Mathf.RoundToInt(Mathf.Clamp(writeFloatValue * 255f, 0, 255)); var mainRead = baseLayerSplat[dx, dz]; var newVal = (byte)Mathf.Clamp(Mathf.Max(writeValue, mainRead), 0, 255); var delta = newVal - mainRead; if (delta < 1 / 255f) { continue; } foreach (var currentPrototype in currentPrototypes) { if (!baseData.ContainsKey(currentPrototype)) { continue; } if (currentPrototype == splatPrototypeWrapper) { continue; } var otherSplatFloatValue = baseData[currentPrototype][dx, dz] / 255f; var otherSplatFloatWriteVal = (otherSplatFloatValue * (1 - (delta / 255f))); var write = (byte)Mathf.Clamp(Mathf.RoundToInt(otherSplatFloatWriteVal * 255), 0, 255); baseData[currentPrototype][dx, dz] = write; } //DebugHelper.DrawPoint(worldPos, 1, Color.red, 10); baseLayerSplat[dx, dz] = newVal; layer.Stencil[coordX, coordZ] = MiscUtilities.CompressStencil(stencilKey, 1); thisPatchStencil[dx, dz] = 1; } else { thisPatchStencil[dx, dz] = 0; } } } } foreach (var existingSplatPrototype in baseData) { var splat = existingSplatPrototype.Key; var data = existingSplatPrototype.Value; layer.SetSplatmap(splat, matrixMin.x, matrixMin.z, data, wrapper.Terrain.terrainData.alphamapResolution, thisPatchStencil); } }
public override void ProcessHeights(TerrainWrapper wrapper, LayerBase baseLayer, int stencilKey) { var layer = baseLayer as MMTerrainLayer; if (layer == null) { Debug.LogWarning(string.Format("Attempted to write {0} to incorrect layer type! Expected Layer {1} to be {2}, but it was {3}", name, baseLayer.name, GetLayerType(), baseLayer.GetType()), this); return; } if (!Network || Configuration == null) { return; } var config = Configuration.GetConfig <Config>(); if (config == null) { Debug.LogError("Invalid configuration! Expected ConnectionTerrainHeightConfiguration"); return; } var terrain = wrapper.Terrain; var terrainPos = wrapper.Terrain.GetPosition(); var terrainSize = wrapper.Terrain.terrainData.size; var heightRes = terrain.terrainData.heightmapResolution; var mainSpline = NodeConnection.GetSpline(); var radius = config.Radius; var falloffCurve = config.Falloff; var heightCurve = config.Height; // Create bounds to encapsulate spline (axis aligned) var bounds = mainSpline.GetApproximateBounds(); bounds.Expand(radius * 2 * Vector3.one); // Create object bounds var objectBounds = mainSpline.GetApproximateXZObjectBounds(); objectBounds.Expand(radius * 2 * Vector3.one); objectBounds.Expand(Vector3.up * 10000); // Early cull var axisBounds = objectBounds.ToAxisBounds(); var terrainBounds = terrain.GetComponent <Collider>().bounds; terrainBounds.Expand(Vector3.up * 10000); if (!terrainBounds.Intersects(axisBounds)) { return; } // Get matrix space min/max var matrixMin = terrain.WorldToHeightmapCoord(bounds.min, TerrainX.RoundType.Floor) - Coord.One; matrixMin = matrixMin.Clamp(0, heightRes); var matrixMax = terrain.WorldToHeightmapCoord(bounds.max, TerrainX.RoundType.Ceil) + Coord.One; matrixMax = matrixMax.Clamp(0, heightRes); var xDelta = matrixMax.x - matrixMin.x; var zDelta = matrixMax.z - matrixMin.z; var floatArraySize = new Common.Coord( Mathf.Min(xDelta, terrain.terrainData.heightmapResolution - matrixMin.x), Mathf.Min(zDelta, terrain.terrainData.heightmapResolution - matrixMin.z)); float planeGive = (wrapper.Terrain.terrainData.size.x / wrapper.Terrain.terrainData.heightmapResolution) * 0; Plane startPlane, endPlane; GenerateSplinePlanes(planeGive, mainSpline, out startPlane, out endPlane); var layerHeights = layer.GetHeights(matrixMin.x, matrixMin.z, floatArraySize.x, floatArraySize.z, heightRes) ?? new Serializable2DFloatArray(floatArraySize.x, floatArraySize.z); stencilKey = GetStencilKey(); if (layer.Stencil == null || layer.Stencil.Width != heightRes || layer.Stencil.Height != heightRes) { layer.Stencil = new Stencil(heightRes, heightRes); } for (var dz = 0; dz < floatArraySize.z; ++dz) { for (var dx = 0; dx < floatArraySize.x; ++dx) { var coordX = matrixMin.x + dx; var coordZ = matrixMin.z + dz; var worldPos = terrain.HeightmapCoordToWorldPos(new Common.Coord(coordX, coordZ)); worldPos = new Vector3(worldPos.x, objectBounds.center.y, worldPos.z); if (!terrain.ContainsPointXZ(worldPos) || !objectBounds.Contains(worldPos) || !GeometryExtensions.BetweenPlanes(worldPos, startPlane, endPlane)) { // Cull if we're outside of the approx bounds continue; } var uniformT = mainSpline.GetClosestUniformTimeOnSplineXZ(worldPos.xz()); // Expensive! var closestOnSpline = mainSpline.GetUniformPointOnSpline(uniformT); var normalizedFlatDistToSpline = (worldPos - closestOnSpline).xz().magnitude / (radius); if (normalizedFlatDistToSpline >= 1) { continue; } var maskValue = Mathf.Clamp01(falloffCurve.Evaluate(normalizedFlatDistToSpline)); var heightDelta = heightCurve.Evaluate(normalizedFlatDistToSpline); float existingStencilStrength; int existingStencilKey; MiscUtilities.DecompressStencil(layer.Stencil[coordX, coordZ], out existingStencilKey, out existingStencilStrength); if (existingStencilKey != stencilKey && existingStencilKey > stencilKey && !(existingStencilStrength < maskValue && maskValue > 0)) { continue; } // Refine our worldposition to be on the same XZ plane as the spline point worldPos = new Vector3(worldPos.x, closestOnSpline.y, worldPos.z); // Find the point on the spline closest to this given point var naturalT = mainSpline.UniformToNaturalTime(uniformT); // Get the upvec from the natural time var up = mainSpline.GetUpVector(naturalT).normalized; // Create a plane and cast against it var plane = new Plane(up, closestOnSpline); float dist = 0; var castRay = new Ray(worldPos, Vector3.down); plane.Raycast(castRay, out dist); var castPoint = castRay.GetPoint(dist); var heightAtPoint = (castPoint.y + heightDelta); heightAtPoint -= terrainPos.y; heightAtPoint /= terrainSize.y; heightAtPoint = MiscUtilities.FloorToUshort(heightAtPoint); var existingHeight = layerHeights[dx, dz]; var newHeight = Mathf.Lerp(existingHeight, heightAtPoint, Mathf.Clamp01(maskValue)); layerHeights[dx, dz] = newHeight; var key = maskValue > existingStencilStrength ? stencilKey : existingStencilKey; var newRawStencilValue = MiscUtilities.CompressStencil(key, /*stencilKey == existingStencilKey ?*/ Mathf.Max(maskValue, existingStencilStrength) /* : maskValue + existingStencilStrength*/); //newRawStencilValue = MiscUtilities.CompressStencil(key, 1); layer.Stencil[coordX, coordZ] = newRawStencilValue; } } layer.SetHeights(matrixMin.x, matrixMin.z, layerHeights, wrapper.Terrain.terrainData.heightmapResolution); }
public override void ProcessStencil(TerrainWrapper terrainWrapper, LayerBase baseLayer, int stencilKey) { var layer = baseLayer as MMTerrainLayer; if (layer == null) { Debug.LogWarning(string.Format("Attempted to write {0} to incorrect layer type! Expected Layer {1} to be {2}, but it was {3}", name, baseLayer.name, GetLayerType(), baseLayer.GetType()), this); return; } stencilKey = GetPriority(); var objectBounds = GetObjectBounds(); var flatObjBounds = objectBounds.Flatten(); var flatBounds = flatObjBounds.ToAxisBounds(); var terrainSize = terrainWrapper.Terrain.terrainData.size; var hRes = terrainWrapper.Terrain.terrainData.heightmapResolution; var matrixMin = terrainWrapper.Terrain.WorldToHeightmapCoord(flatBounds.min, TerrainX.RoundType.Floor); var matrixMax = terrainWrapper.Terrain.WorldToHeightmapCoord(flatBounds.max, TerrainX.RoundType.Ceil); matrixMin = new Common.Coord(Mathf.Clamp(matrixMin.x, 0, hRes), Mathf.Clamp(matrixMin.z, 0, hRes)); matrixMax = new Common.Coord(Mathf.Clamp(matrixMax.x, 0, hRes), Mathf.Clamp(matrixMax.z, 0, hRes)); var floatArraySize = new Common.Coord(matrixMax.x - matrixMin.x, matrixMax.z - matrixMin.z); layer.BlendMode = MMTerrainLayer.EMMTerrainLayerBlendMode.Stencil; if (layer.Stencil == null || layer.Stencil.Width != hRes || layer.Stencil.Height != hRes) { layer.Stencil = new Stencil(hRes, hRes); } var layerHeights = layer.GetHeights(matrixMin.x, matrixMin.z, floatArraySize.x, floatArraySize.z, hRes) ?? new Serializable2DFloatArray(floatArraySize.x, floatArraySize.z); var objectBoundsPlane = new Plane((objectBounds.Rotation * Vector3.up).normalized, objectBounds.center); for (var dz = 0; dz < floatArraySize.z; ++dz) { for (var dx = 0; dx < floatArraySize.x; ++dx) { var coordX = matrixMin.x + dx; var coordZ = matrixMin.z + dz; int existingStencilKey; float existingStencilVal; MiscUtilities.DecompressStencil(layer.Stencil[coordX, coordZ], out existingStencilKey, out existingStencilVal); var worldPos = terrainWrapper.Terrain.HeightmapCoordToWorldPos(new Common.Coord(coordX, coordZ)); worldPos = new Vector3(worldPos.x, objectBounds.center.y, worldPos.z); if (!flatObjBounds.Contains(new Vector3(worldPos.x, flatObjBounds.center.y, worldPos.z))) { continue; } var localPos = Quaternion.Inverse(objectBounds.Rotation) * (worldPos - objectBounds.min); var xDist = localPos.x / objectBounds.size.x; var zDist = localPos.z / objectBounds.size.z; float falloff = GetFalloff(new Vector2(xDist, zDist)); if (Mathf.Approximately(falloff, 0)) { continue; } var planeRay = new Ray(worldPos, Vector3.up); float dist; objectBoundsPlane.Raycast(planeRay, out dist); var heightAtPoint = (planeRay.GetPoint(dist) - terrainWrapper.transform.position).y / terrainSize.y; var blendedHeight = heightAtPoint; var existingHeight = layerHeights[dx, dz]; if (SetHeights) { if (BlendMode == HeightBlendMode.Max && existingHeight > heightAtPoint) { layer.Stencil[matrixMin.x + dx, matrixMin.z + dz] = MiscUtilities.CompressStencil(0, 0); continue; } if (BlendMode == HeightBlendMode.Min && existingHeight < heightAtPoint) { layer.Stencil[matrixMin.x + dx, matrixMin.z + dz] = MiscUtilities.CompressStencil(0, 0); continue; } } switch (BlendMode) { case HeightBlendMode.Set: blendedHeight = Mathf.Lerp(existingHeight, blendedHeight, falloff); break; case HeightBlendMode.Max: blendedHeight = Mathf.Max(existingHeight, blendedHeight); break; case HeightBlendMode.Min: blendedHeight = Mathf.Min(existingHeight, blendedHeight); break; case HeightBlendMode.Average: blendedHeight = (existingHeight + blendedHeight) / 2; break; } layer.Stencil[matrixMin.x + dx, matrixMin.z + dz] = MiscUtilities.CompressStencil(stencilKey, 1); } } }