public static IEnumerator Apply(CoordRect rect, Terrain terrain, object dataBox, Func<float,bool> stop= null) { #if __MEGASPLAT__ //loading objects MegaSplatData tuple = (MegaSplatData)dataBox; if (tuple == null) yield break; //terrain.materialType = Terrain.MaterialType.Custom; //it's already done with MapMagic //terrain.materialTemplate = new Material(tuple.template); // TODO: We should pool these textures instead of creating and destroying them! int res = MapMagic.instance.resolution; //control texture var control = new Texture2D(res, res, MegaSplatOutput.formatARGB? TextureFormat.ARGB32 : TextureFormat.RGBA32, false, true); control.wrapMode = TextureWrapMode.Clamp; control.filterMode = FilterMode.Point; control.SetPixels(0, 0, control.width, control.height, tuple.control); control.Apply(); yield return null; //param texture var paramTex = new Texture2D(res, res, MegaSplatOutput.formatARGB? TextureFormat.ARGB32 : TextureFormat.RGBA32, false, true); paramTex.wrapMode = TextureWrapMode.Clamp; paramTex.filterMode = FilterMode.Point; paramTex.SetPixels(0, 0, paramTex.width, paramTex.height, tuple.param); paramTex.Apply(); yield return null; //welding if (MapMagic.instance != null && MapMagic.instance.splatsWeldMargins!=0) { Coord coord = Coord.PickCell(rect.offset, MapMagic.instance.resolution); //Chunk chunk = MapMagic.instance.chunks[coord.x, coord.z]; Chunk neigPrevX = MapMagic.instance.chunks[coord.x-1, coord.z]; if (neigPrevX!=null && neigPrevX.worker.ready && neigPrevX.terrain.materialTemplate.HasProperty("_SplatControl")) { WeldTerrains.WeldTextureToPrevX(control, (Texture2D)neigPrevX.terrain.materialTemplate.GetTexture("_SplatControl")); control.Apply(); } Chunk neigNextX = MapMagic.instance.chunks[coord.x+1, coord.z]; if (neigNextX!=null && neigNextX.worker.ready && neigNextX.terrain.materialTemplate.HasProperty("_SplatControl")) { WeldTerrains.WeldTextureToNextX(control, (Texture2D)neigNextX.terrain.materialTemplate.GetTexture("_SplatControl")); control.Apply(); } Chunk neigPrevZ = MapMagic.instance.chunks[coord.x, coord.z-1]; if (neigPrevZ!=null && neigPrevZ.worker.ready && neigPrevZ.terrain.materialTemplate.HasProperty("_SplatControl")) { WeldTerrains.WeldTextureToPrevZ(control, (Texture2D)neigPrevZ.terrain.materialTemplate.GetTexture("_SplatControl")); control.Apply(); } Chunk neigNextZ = MapMagic.instance.chunks[coord.x, coord.z+1]; if (neigNextZ!=null && neigNextZ.worker.ready && neigNextZ.terrain.materialTemplate.HasProperty("_SplatControl")) { WeldTerrains.WeldTextureToNextZ(control, (Texture2D)neigNextZ.terrain.materialTemplate.GetTexture("_SplatControl")); control.Apply(); } } yield return null; //TODO: weld textures with 1-pixel margin //assign textures using material property (not saving for fixed terrains) //#if UNITY_5_5_OR_NEWER //MaterialPropertyBlock matProp = new MaterialPropertyBlock(); //matProp.SetTexture("_SplatControl", control); //matProp.SetTexture("_SplatParams", paramTex); //matProp.SetFloat("_ControlSize", res); //terrain.SetSplatMaterialPropertyBlock(matProp); //#endif //duplicating material if (MapMagic.instance.customTerrainMaterial != null) { terrain.materialTemplate = new Material(MapMagic.instance.customTerrainMaterial); //assigning control textures if (terrain.materialTemplate.HasProperty("_SplatControl")) terrain.materialTemplate.SetTexture("_SplatControl", control); if (terrain.materialTemplate.HasProperty("_SplatParams")) terrain.materialTemplate.SetTexture("_SplatParams", paramTex); } #else yield return null; #endif }
public static void Process(CoordRect rect, Chunk.Results results, GeneratorsAsset gens, Chunk.Size terrainSize, Func<float,bool> stop = null) { #if __MEGASPLAT__ if (stop!=null && stop(0)) return; //using the first texture list for all MegaSplatTextureList textureList = null; bool smoothFallof = false; float clusterScale = 0.05f; foreach (MegaSplatOutput gen in gens.GeneratorsOfType<MegaSplatOutput>(onlyEnabled: true, checkBiomes: true)) { if (gen.textureList != null) textureList = gen.textureList; smoothFallof = gen.smoothFallof; clusterScale = gen.clusterNoiseScale; } //creating color arrays MegaSplatData result = new MegaSplatData(); result.control = new Color[MapMagic.instance.resolution * MapMagic.instance.resolution]; result.param = new Color[MapMagic.instance.resolution * MapMagic.instance.resolution]; //creating all and special layers/biomes lists List<Layer> allLayers = new List<Layer>(); //all layers count = gen num * layers num in each gen (excluding empty biomes, matrices, etc) List<Matrix> allMatrices = new List<Matrix>(); List<Matrix> allBiomeMasks = new List<Matrix>(); List<Matrix> specialWetnessMatrices = new List<Matrix>(); //special count = number of generators (excluding empty biomes only) List<Matrix> specialPuddlesMatrices = new List<Matrix>(); List<Matrix> specialDampeningMatrices = new List<Matrix>(); List<Matrix> specialBiomeMasks = new List<Matrix>(); //filling all layers/biomes foreach (MegaSplatOutput gen in gens.GeneratorsOfType<MegaSplatOutput>(onlyEnabled: true, checkBiomes: true)) { gen.textureList = textureList; //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 } for (int i = 0; i < gen.baseLayers.Length; i++) { //reading output directly Output output = gen.baseLayers[i].output; if (stop!=null && stop(0)) return; //checking stop before reading output if (!results.results.ContainsKey(output)) continue; Matrix matrix = (Matrix)results.results[output]; if (matrix.IsEmpty()) continue; if (i >= textureList.clusters.Length) { Debug.LogError("Cluster out of range"); continue; } //adding to lists allLayers.Add(gen.baseLayers[i]); allMatrices.Add(matrix); allBiomeMasks.Add(gen.biome == null ? null : biomeMask); } //adding special object wetnessObj = gen.wetnessIn.GetObject(results); specialWetnessMatrices.Add( wetnessObj!=null? (Matrix)wetnessObj : null ); object puddlesObj = gen.puddlesIn.GetObject(results); specialPuddlesMatrices.Add( puddlesObj!=null? (Matrix)puddlesObj : null ); object dampeingObj = gen.displaceDampenIn.GetObject(results); specialDampeningMatrices.Add( dampeingObj!=null? (Matrix)dampeingObj : null ); specialBiomeMasks.Add(gen.biome == null ? null : biomeMask); } //if no texture list found in any of generators - returning if (textureList == null || allLayers.Count==0) return; //processing int allLayersCount = allLayers.Count; int specialCount = specialWetnessMatrices.Count; for (int x = 0; x<rect.size.x; x++) for (int z = 0; z<rect.size.z; z++) { int pos = rect.GetPos(x + rect.offset.x, z + rect.offset.z); // doesn't use height, normal, but I'm not sure how to get that here.. Vector3 worldPos = new Vector3( 1f * (x+rect.offset.x) / MapMagic.instance.resolution * rect.size.x, 0, 1f * (z+rect.offset.z) / MapMagic.instance.resolution * rect.size.z); float heightRatio = results.heights!=null? results.heights.array[pos] : 0.5f; //0 is the bottom point, 1 is the maximum top Vector3 normal = new Vector3(0,1,0); // find highest two layers int botIdx = 0; int topIdx = 0; float botWeight = 0; float topWeight = 0; for (int i = 0; i<allLayersCount; i++) { float val = allMatrices[i].array[pos]; if (allBiomeMasks[i] != null) val *= allBiomeMasks[i].array[pos]; // really want world position, Normal, and height ratio for brushes, but for now, just use x/z.. if (val > botWeight) { topWeight = botWeight; topIdx = botIdx; botWeight = val; botIdx = i; } else if (val > topWeight) { topIdx = i; topWeight = val; } } //converting layer index to texture index topIdx = textureList.clusters[ allLayers[topIdx].index ].GetIndex(worldPos * clusterScale, normal, heightRatio); botIdx = textureList.clusters[ allLayers[botIdx].index ].GetIndex(worldPos * clusterScale, normal, heightRatio); //swapping indexes to make topIdx always on top if (botIdx > topIdx) { int tempIdx = topIdx; topIdx = botIdx; botIdx = tempIdx; float tempWeight = topWeight; topWeight = botWeight; botWeight = tempWeight; } //finding blend float totalWeight = topWeight + botWeight; if (totalWeight<0.01f) totalWeight = 0.01f; //Mathf.Max and Clamp are slow float blend = botWeight / totalWeight; if (blend>1) blend = 1; //adjusting blend curve if (smoothFallof) blend = (Mathf.Sqrt(blend) * (1-blend)) + blend*blend*blend; //Magic secret formula! Inverse to 3*x^2 - 2*x^3 //setting color result.control[pos] = new Color(botIdx / 255.0f, topIdx / 255.0f, 1.0f - blend, 1.0f); //params for (int i = 0; i<specialCount; i++) { float biomeVal = specialBiomeMasks[i]!=null? specialBiomeMasks[i].array[pos] : 1; if (specialWetnessMatrices[i]!=null) result.param[pos].b = specialWetnessMatrices[i].array[pos] * biomeVal; if (specialPuddlesMatrices[i]!=null) { result.param[pos].a = specialPuddlesMatrices[i].array[pos] * biomeVal; result.param[pos].r = 0.5f; result.param[pos].g = 0.5f; } if (specialDampeningMatrices[i]!=null) result.control[pos].a = specialDampeningMatrices[i].array[pos] * biomeVal; } } //pushing to apply if (stop!=null && stop(0)) return; results.apply.CheckAdd(typeof(MegaSplatOutput), result, replace: true); #endif }