public override void Generate(CoordRect rect, Chunk.Results results, Chunk.Size terrainSize, int seed, Func <float, bool> stop = null) { Matrix src = (Matrix)input.GetObject(results); if (src == null || (stop != null && stop(0))) { return; } if (!enabled) { output.SetObject(results, src); return; } Matrix dst = new Matrix(src.rect); Coord min = src.rect.Min; Coord max = src.rect.Max; for (int x = min.x; x < max.x; x++) { for (int z = min.z; z < max.z; z++) { float val = level - src[x, z]; dst[x, z] = val > 0? val : 0; } } if (stop != null && stop(0)) { return; } output.SetObject(results, dst); }
public override void Generate(CoordRect rect, Chunk.Results results, Chunk.Size terrainSize, int seed, Func <float, bool> stop = null) { object obj = null; if (input.link != null && enabled) { obj = input.GetObject(results); } else { if (type == InoutType.Map) { obj = new Matrix(rect); } if (type == InoutType.Objects) { obj = new SpatialHash(new Vector2(rect.offset.x, rect.offset.z), rect.size.x, 16); } } if (stop != null && stop(0)) { return; } output.SetObject(results, obj); }
public override void Generate(CoordRect rect, Chunk.Results results, Chunk.Size terrainSize, int seed, Func <float, bool> stop = null) { //getting input Matrix src = (Matrix)input.GetObject(results); //return on stop/disable/null input if (stop != null && stop(0)) { return; } if (!enabled || src == null) { output.SetObject(results, src); return; } //preparing output Matrix dst = src.Copy(null); float minClamp = min / MapMagic.instance.terrainHeight; float maxClamp = max / MapMagic.instance.terrainHeight; for (int i = 0; i < dst.array.Length; i++) { if (!unitClamp) { if (dst.array[i] < minClamp) { dst.array[i] = minClamp; } if (dst.array[i] > maxClamp) { dst.array[i] = maxClamp; } } else { dst.array[i] = dst.array[i] > minClamp && dst.array[i] < maxClamp ? 1 : 0; } } //mask and safe borders if (stop != null && stop(0)) { return; } Matrix mask = (Matrix)maskIn.GetObject(results); if (mask != null) { Matrix.Mask(src, dst, mask); } //setting output if (stop != null && stop(0)) { return; } output.SetObject(results, dst); }
public override void Generate(CoordRect rect, Chunk.Results results, Chunk.Size terrainSize, int seed, Func <float, bool> stop = null) { #if RTP if ((stop != null && stop(0)) || !enabled) { return; } //loading inputs Matrix[] matrices = new Matrix[baseLayers.Length]; for (int i = 0; i < baseLayers.Length; i++) { if (baseLayers[i].input != null) { matrices[i] = (Matrix)baseLayers[i].input.GetObject(results); if (matrices[i] != null) { matrices[i] = matrices[i].Copy(null); } } if (matrices[i] == null) { matrices[i] = new Matrix(rect); } } if (matrices.Length == 0) { return; } //background matrix //matrices[0] = terrain.defaultMatrix; //already created matrices[0].Fill(1); //populating opacity array float[] opacities = new float[matrices.Length]; for (int i = 0; i < baseLayers.Length; i++) { opacities[i] = baseLayers[i].opacity; } opacities[0] = 1; //blending layers Matrix.BlendLayers(matrices, opacities); //saving changed matrix results for (int i = 0; i < baseLayers.Length; i++) { if (stop != null && stop(0)) { return; //do not write object is generating is stopped } baseLayers[i].output.SetObject(results, matrices[i]); } #endif }
public virtual void Generate(CoordRect rect, Chunk.Results results, Chunk.Size terrainSize, int seed, Func <float, bool> stop = null) { }
public static void Process(CoordRect rect, Chunk.Results results, GeneratorsAsset gens, Chunk.Size terrainSize, Func <float, bool> stop = null) { #if RTP if (stop != null && stop(0)) { return; } //finding number of layers int layersCount = 0; foreach (RTPOutput gen in MapMagic.instance.gens.GeneratorsOfType <RTPOutput>(onlyEnabled:true, checkBiomes:true)) { layersCount = gen.baseLayers.Length; break; } //creating color arrays RTPTuple result = new RTPTuple(); result.colorsA = new Color[MapMagic.instance.resolution * MapMagic.instance.resolution]; if (layersCount > 4) { result.colorsB = new Color[MapMagic.instance.resolution * MapMagic.instance.resolution]; } //filling color arrays foreach (RTPOutput gen in MapMagic.instance.gens.GeneratorsOfType <RTPOutput>(onlyEnabled:true, checkBiomes:true)) { //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; } for (int x = 0; x < rect.size.x; x++) { for (int z = 0; z < rect.size.z; z++) { int pos = matrix.rect.GetPos(x + matrix.rect.offset.x, z + matrix.rect.offset.z); //pos should be the same for colors array and matrix array //get value and adjust with biome mask float val = matrix.array[pos]; float biomeVal = biomeMask != null? biomeMask.array[pos] : 1; val *= biomeVal; //save value to colors array switch (gen.baseLayers[i].index) { case 0: result.colorsA[pos].r += val; break; case 1: result.colorsA[pos].g += val; break; case 2: result.colorsA[pos].b += val; break; case 3: result.colorsA[pos].a += val; break; case 4: result.colorsB[pos].r += val; break; case 5: result.colorsB[pos].g += val; break; case 6: result.colorsB[pos].b += val; break; case 7: result.colorsB[pos].a += val; break; } } } if (stop != null && stop(0)) { return; } } } //TODO: normalizing color arrays (if needed) //pushing to apply if (stop != null && stop(0)) { return; } results.apply.CheckAdd(typeof(RTPOutput), result, replace: true); #endif }
public static Voxeland5.Voxeland voxeland; //TODO static is not serialized //TODO: somehow assign voxeland on data asssign or window open #endif //[System.Diagnostics.Conditional("VOXELAND")] //wtf it does not work! public override void Generate(CoordRect rect, Chunk.Results results, Chunk.Size terrainSize, int seed, Func <float, bool> stop = null) { #if VOXELAND if (stop != null && stop(0)) { return; } //finding instance //Voxeland5.Voxeland voxeland = null; //foreach (Voxeland5.Voxeland v in Voxeland5.Voxeland.instances) // if (v.data.generator.mapMagicGens.ContainsGenerator(this)) voxeland = v; if (voxeland == null) { return; } //preparing random //Noise noise = new Noise(123, permutationCount:512); //random to floor floats //preparing area Voxeland5.Data.Area area = new Voxeland5.Data.Area(); area.Init(rect.offset.x / terrainSize.resolution, rect.offset.z / terrainSize.resolution, terrainSize.resolution, null); //TODO get height factor int heightFactor = 200; //iterating layers for (int l = 0; l < layers.Length; l++) { Layer layer = layers[l]; if (!layer.enabled) { continue; } int blockType = layer.blockType; if (blockType >= voxeland.landTypes.array.Length) { blockType = Voxeland5.Data.emptyByte; } //loading inputs Matrix src = (Matrix)layer.input.GetObject(results); Matrix heightSrc = (Matrix)layer.heightInput.GetObject(results); //SpatialHash objectsSrc = (SpatialHash)layer.objectInput.GetObject(results); if (src == null) { continue; } //apply switch (layer.applyType) { //case Voxeland5.Generator.LayerOverlayType.add: area.AddLayer(src, blockType, heightFactor:heightFactor, noise:noise); break; //case Voxeland5.Generator.LayerOverlayType.clampAppend: area.ClampAppendLayer(src, blockType, heightFactor:heightFactor, noise:noise); break; //case Voxeland5.Generator.LayerOverlayType.absolute: area.SetLayer(src, heightSrc, blockType, heightFactor:heightFactor, noise:noise); break; //case Voxeland5.Generator.LayerOverlayType.paint: area.PaintLayer(src, blockType, paintThickness:layer.paintThickness, noise:noise); break; case Voxeland5.Generator.LayerOverlayType.add: area.AddLayer(src.rect.offset.x, src.rect.offset.z, src.rect.size.x, src.array, blockType, heightFactor: heightFactor); break; case Voxeland5.Generator.LayerOverlayType.clampAppend: area.ClampAppendLayer(src.rect.offset.x, src.rect.offset.z, src.rect.size.x, src.array, blockType, heightFactor: heightFactor); break; case Voxeland5.Generator.LayerOverlayType.absolute: area.SetLayer(src.rect.offset.x, src.rect.offset.z, src.rect.size.x, src.array, heightSrc != null? heightSrc.array : null, blockType, heightFactor: heightFactor); break; case Voxeland5.Generator.LayerOverlayType.paint: area.PaintLayer(src.rect.offset.x, src.rect.offset.z, src.rect.size.x, src.array, blockType, paintThickness: layer.paintThickness); break; } } //getting outputs MultiDict <int, int> typeToLayer = new MultiDict <int, int>(); for (int l = 0; l < layers.Length; l++) { typeToLayer.Add(layers[l].blockType, l); } Matrix[] layerMatrices = new Matrix[layers.Length]; for (int l = 0; l < layerMatrices.Length; l++) { layerMatrices[l] = new Matrix(rect); } for (int x = 0; x < rect.size.x; x++) { Voxeland5.Data.Area.Line line = area.lines[x]; for (int z = 0; z < rect.size.z; z++) { int topType = line.columns[z].topType; if (topType == Voxeland5.Data.emptyByte) { continue; //empty column } List <int> typeLayerNums = typeToLayer[topType]; #if WDEBUG if (typeLayerNums == null) { Debug.LogError("This should not happen " + topType); } #endif int matrixPos = z * rect.size.x + x; for (int m = 0; m < typeLayerNums.Count; m++) { layerMatrices[typeLayerNums[m]].array[matrixPos] = 1; } } } for (int l = 0; l < layers.Length; l++) { layers[l].output.SetObject(results, layerMatrices[l]); } //saving results areaOutput.SetObject(results, area); #endif }
public static void Process(CoordRect rect, Chunk.Results results, GeneratorsAsset gens, Chunk.Size terrainSize, Func <float, bool> stop = null) { #if VOXELAND if (stop != null && stop(0)) { return; } if (voxeland == null) { return; } //finding area by rect offset Coord areaCoord = Coord.PickCell(rect.offset.x, rect.offset.z, voxeland.data.areaSize); Voxeland5.Data.Area area = voxeland.data.areas[areaCoord.x, areaCoord.z]; //clearing grass area.ClearGrass(); //preparing random //Noise noise = new Noise(12345); //to switch grass depending on it's opacity //processing foreach (VoxelandGrassOutput gen in gens.GeneratorsOfType <VoxelandGrassOutput>(onlyEnabled:true, checkBiomes:true)) { //reading output directly if (stop != null && stop(0)) { return; //checking stop before reading output } //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 } } //iterating layers for (int l = 0; l < gen.layers.Length; l++) { Layer layer = gen.layers[l]; //loading inputs Matrix src = (Matrix)layer.input.GetObject(results); if (src == null) { continue; } //multiplying with biome mask - in SetGrassLayer //apply //area.SetGrassLayer(src, (byte)l, layer.density, noise:noise, layerNum:l, mask:biomeMask); area.SetGrassLayer(src.rect.offset.x, src.rect.offset.z, src.rect.size.x, src.array, (byte)l, layer.density, l, biomeMask == null? null : biomeMask.array); } } //pushing to apply if (stop != null && stop(0)) { return; } results.apply.CheckAdd(typeof(VoxelandOutput), null, replace: true); #endif }
public static void Process(CoordRect rect, Chunk.Results results, GeneratorsAsset gens, Chunk.Size terrainSize, Func <float, bool> stop = null) { #if VOXELAND if (stop != null && stop(0)) { return; } if (voxeland == null) { return; } //TODO get height factor int heightFactor = 200; //finding area by rect offset Coord areaCoord = Coord.PickCell(rect.offset.x, rect.offset.z, voxeland.data.areaSize); Voxeland5.Data.Area area = voxeland.data.areas[areaCoord.x, areaCoord.z]; //clearing objects area.ClearObjects(); //preparing random Noise noise = new Noise(12345); //to disable biome objects //processing foreach (VoxelandObjectsOutput gen in gens.GeneratorsOfType <VoxelandObjectsOutput>(onlyEnabled:true, checkBiomes:true)) { //reading output directly //Output output = gen.areaOutput; if (stop != null && stop(0)) { return; //checking stop before reading output } //if (!results.results.ContainsKey(output)) continue; //Voxeland5.Data.Area genArea = (Voxeland5.Data.Area)results.results[output]; //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 } } //iterating layers for (int l = 0; l < gen.layers.Length; l++) { Layer layer = gen.layers[l]; //loading inputs SpatialHash src = (SpatialHash)layer.input.GetObject(results); if (src == null) { continue; } foreach (SpatialObject obj in src.AllObjs()) { int objX = (int)(obj.pos.x + 0.5f); int objZ = (int)(obj.pos.y + 0.5f); //biome masking float biomeVal = 1; if (gen.biome != null) { if (biomeMask == null) { biomeVal = 0; } else { biomeVal = biomeMask[objX, objZ]; } } if (biomeVal < noise.Random(objX, objZ)) { continue; } //flooring float terrainHeight = layer.relativeHeight? results.heights[objX, objZ] : 0; int objHeight = (int)((obj.height + terrainHeight) * heightFactor + 0.5f); //area.AddObject(new CoordDir(objX, objHeight, objZ), (short)l); area.AddObject(objX, objHeight, objZ, 0, (short)l); } } } //pushing to apply if (stop != null && stop(0)) { return; } results.apply.CheckAdd(typeof(VoxelandOutput), null, replace: true); #endif }
public static void Process(CoordRect rect, Chunk.Results results, GeneratorsAsset gens, Chunk.Size terrainSize, Func <float, bool> stop = null) { #if VOXELAND if (stop != null && stop(0)) { return; } if (voxeland == null) { return; } //TODO get height factor int heightFactor = 200; //finding area by rect offset Coord areaCoord = Coord.PickCell(rect.offset.x, rect.offset.z, voxeland.data.areaSize); Voxeland5.Data.Area area = voxeland.data.areas[areaCoord.x, areaCoord.z]; //clearing area area.ClearLand(); //finding a list of areas and their opacities List <Voxeland5.Data.Area> areas = new List <Voxeland5.Data.Area>(); List <Matrix> opacities = new List <Matrix>(); foreach (VoxelandOutput gen in gens.GeneratorsOfType <VoxelandOutput>(onlyEnabled:true, checkBiomes:true)) { //reading output directly Output output = gen.areaOutput; if (stop != null && stop(0)) { return; //checking stop before reading output } if (!results.results.ContainsKey(output)) { continue; } Voxeland5.Data.Area genArea = (Voxeland5.Data.Area)results.results[output]; //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 } } areas.Add(genArea); opacities.Add(biomeMask); } //merge areas using biome mask if (areas.Count >= 2) //area.MixAreas(areas.ToArray(), opacities.ToArray()); { float[][] opacityArrays = new float[opacities.Count][]; for (int i = 0; i < opacityArrays.Length; i++) { if (opacities[i] != null) { opacityArrays[i] = opacities[i].array; } } area.MixAreas(areas.ToArray(), rect.offset.x, rect.offset.z, rect.size.x, opacityArrays); } else { Voxeland5.Data.Area.CopyLand(areas[0], area); } //reading heights if (results.heights == null || results.heights.rect.size.x != rect.size.x) { results.heights = new Matrix(rect); } if (results.heights.rect != rect) { results.heights.Resize(rect); } results.heights.Clear(); for (int x = 0; x < results.heights.rect.size.x; x++) { for (int z = 0; z < results.heights.rect.size.z; z++) { results.heights[x + results.heights.rect.offset.x, z + results.heights.rect.offset.z] = 1f * area.lines[x].columns[z].topLevel / heightFactor; } } //pushing to apply if (stop != null && stop(0)) { return; } results.apply.CheckAdd(typeof(VoxelandOutput), null, replace: true); #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 }
public static void Process(CoordRect rect, Chunk.Results results, GeneratorsAsset gens, Chunk.Size terrainSize, Func <float, bool> stop = null) { #if VEGETATION_STUDIO if (stop != null && stop(0)) { return; } //preparing and clearing storage if (vetStorComponents == null || vetStorComponents.Count == 0) { return; //vs not used (process runs anyway) } PersistentVegetationStorage storage = vetStorComponents[rect]; int cellXCount = Mathf.CeilToInt(terrainSize.dimensions / cellSize); int cellZCount = Mathf.CeilToInt(terrainSize.dimensions / cellSize); Noise noise = new Noise(12345, permutationCount: 128); //to pick objects based on biome //clearing all of the items foreach (VegetationStudioOutput gen in gens.GeneratorsOfType <VegetationStudioOutput>(onlyEnabled:true, checkBiomes:true)) { for (int b = 0; b < gen.layers.Length; b++) { string id = gen.package.VegetationInfoList[b].VegetationItemID; storage.RemoveVegetationItemInstances(id, VS_MM_id); } break; //iterating in one generator only - they use the same layers } if (stop != null && stop(0)) { return; } //object outputs foreach (VegetationStudioOutput gen in gens.GeneratorsOfType <VegetationStudioOutput>(onlyEnabled:true, checkBiomes:true)) { //gen biome mask 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 } } //iterating in layers for (int b = 0; b < gen.layers.Length; b++) { if (stop != null && stop(0)) { return; //checking stop before reading output } Layer layer = gen.layers[b]; string id = gen.package.VegetationInfoList[b].VegetationItemID; //objects layer if (layer.type == Layer.Type.Object) { //loading objects from input SpatialHash hash = (SpatialHash)gen.layers[b].objInput.GetObject(results); if (hash == null) { continue; } //filling instances (no need to check/add key in multidict) foreach (SpatialObject obj in hash.AllObjs()) { //skipping on biome not used float biomeFactor = 0; if (gen.biome == null) { biomeFactor = 1; } else if (biomeMask != null) { biomeFactor = biomeMask.GetInterpolated(obj.pos.x, obj.pos.y); } if (biomeFactor < 0.00001f) { continue; } float rnd; switch (biomeBlendType) { case ObjectOutput.BiomeBlendType.Sharp: rnd = 0.5f; break; case ObjectOutput.BiomeBlendType.AdditiveRandom: case ObjectOutput.BiomeBlendType.NormalizedRandom: rnd = noise.Random((int)obj.pos.x, (int)obj.pos.y); if (biomeFactor > 0.5f) { rnd = 1 - rnd; //test } break; case ObjectOutput.BiomeBlendType.Scale: rnd = 0.0f; break; default: rnd = 0.5f; break; } if (biomeFactor < rnd) { continue; } //flooring float terrainHeight = 0; if (layer.relativeHeight && results.heights != null) //if checbox enabled and heights exist (at least one height generator is in the graph) { terrainHeight = results.heights.GetInterpolated(obj.pos.x, obj.pos.y); } if (terrainHeight > 1) { terrainHeight = 1; } //terrain-space object position Vector3 position = new Vector3( (obj.pos.x - hash.offset.x) / hash.size * terrainSize.dimensions, (obj.height + terrainHeight) * terrainSize.height, (obj.pos.y - hash.offset.y) / hash.size * terrainSize.dimensions); //cell number int cx = (int)(position.x / cellSize); int cz = (int)(position.z / cellSize); PersistentVegetationCell cell = storage.PersistentVegetationStoragePackage.PersistentVegetationCellList[cz + cx * cellXCount]; //rotation + taking terrain normal Quaternion rotation; float objRotation = layer.rotate ? obj.rotation % 360 : 0; if (layer.takeTerrainNormal) { Vector3 terrainNormal = ObjectOutput.GetTerrainNormal(obj.pos.x, obj.pos.y, results.heights, terrainSize.height, terrainSize.pixelSize); Vector3 sideVector = new Vector3(Mathf.Sin((obj.rotation + 90) * Mathf.Deg2Rad), 0, Mathf.Cos((obj.rotation + 90) * Mathf.Deg2Rad)); Vector3 frontVector = Vector3.Cross(sideVector, terrainNormal); rotation = Quaternion.LookRotation(frontVector, terrainNormal); } else { rotation = objRotation.EulerToQuat(); } //scale + biome scale mode Vector3 scale = layer.scale ? new Vector3(layer.scaleY ? 1 : obj.size, obj.size, layer.scaleY ? 1 : obj.size) : Vector3.one; if (biomeBlendType == ObjectOutput.BiomeBlendType.Scale && gen.biome != null) { float biomeVal = 1; if (biomeMask != null) { biomeVal = biomeMask[obj.pos]; } if (biomeVal < 0.001f) { continue; //skip zero-scaled objects } scale *= biomeVal; } //storage.AddVegetationItemInstance(id, position, scale, rotation, layer.applyMeshRotation, VS_MM_id, true); cell.AddVegetationItemInstance(id, position, scale, rotation, VS_MM_id); } if (stop != null && stop(0)) { return; } } //map outputs if (layer.type == Layer.Type.Map) { //reading output directly //Output output = gen.layers[b].output; //if (stop!=null && stop(0)) return; //checking stop before reading output //if (!results.results.ContainsKey(output)) continue; //Matrix matrix = (Matrix)results.results[output]; //loading from input if (stop != null && stop(0)) { return; } Matrix matrix = (Matrix)gen.layers[b].mapInput.GetObject(results); if (matrix == null) { continue; } Matrix heights = results.heights; //get heights before the chunk is removed //setting bush by bush using the sample dist float sampleDist = 1f / layer.density; //filling float terrainPosX = 1f * rect.offset.x / terrainSize.resolution * terrainSize.dimensions; float terrainPosZ = 1f * rect.offset.z / terrainSize.resolution * terrainSize.dimensions; for (int cx = 0; cx <= cellXCount - 1; cx++) { for (int cz = 0; cz <= cellZCount - 1; cz++) { Vector3 cellCorner = new Vector3(terrainPosX + (cellSize * cx), 0, terrainPosZ + (cellSize * cz)); PersistentVegetationCell cell = storage.PersistentVegetationStoragePackage.PersistentVegetationCellList[cz + cx * cellXCount]; for (float x = 0; x < cellSize; x += sampleDist) { for (float z = 0; z < cellSize; z += sampleDist) { //world position float wx = cellSize * cx + x; float wz = cellSize * cz + z; //randomizing position wx += noise.Random((int)(wx * 10), (int)(wz * 10), 2) * sampleDist - sampleDist / 2; wz += noise.Random((int)(wx * 10), (int)(wz * 10), 3) * sampleDist - sampleDist / 2; //map position float mx = wx / terrainSize.dimensions * rect.size.x + rect.offset.x; // relative (0-1) position * terrain res float mz = wz / terrainSize.dimensions * rect.size.z + rect.offset.z; float val = matrix.GetInterpolated(mx, mz); float biomeFactor = 0; if (gen.biome == null) { biomeFactor = 1; } else if (biomeMask != null) { biomeFactor = biomeMask.GetInterpolated(mx, mz); } //placing object float rnd = (noise.Random((int)(wx * 10), (int)(wz * 10))); if (rnd < val * biomeFactor) { float terrainHeight = heights.GetInterpolated(mx, mz) * terrainSize.height; //rotation + taking terrain normal Quaternion rotation; float rotRnd = noise.Random((int)(wx * 10), (int)(wz * 10), 1); float objRotation = layer.rotate ? rotRnd * 360 : 0; if (layer.takeTerrainNormal) { Vector3 terrainNormal = ObjectOutput.GetTerrainNormal(mx, mz, heights, terrainSize.height, terrainSize.pixelSize); Vector3 sideVector = new Vector3(Mathf.Sin((objRotation + 90) * Mathf.Deg2Rad), 0, Mathf.Cos((objRotation + 90) * Mathf.Deg2Rad)); Vector3 frontVector = Vector3.Cross(sideVector, terrainNormal); rotation = Quaternion.LookRotation(frontVector, terrainNormal); } else { rotation = objRotation.EulerToQuat(); } //scale float rndScale = noise.Random((int)(wx * 10), (int)(wz * 10), 1); rndScale = layer.scaleMinMax.x + (layer.scaleMinMax.y - layer.scaleMinMax.x) * rndScale; Vector3 scale = new Vector3(rndScale, rndScale, rndScale); //storage.AddVegetationItemInstance(id, new Vector3(wx,terrainHeight,wz), scale, rotation, layer.applyMeshRotation, VS_MM_id, true); cell.AddVegetationItemInstance(id, new Vector3(wx, terrainHeight, wz), scale, rotation, VS_MM_id); } } } if (stop != null && stop(0)) { return; } } } } } } //refreshing billboards //calling it from thread ruins all the billboards //BillboardSystem billboardSys = billboardComponents[rect]; //if (billboardSys != null) // billboardSys.RefreshBillboards(); #endif //pushing anything to apply if (stop != null && stop(0)) { return; } results.apply.CheckAdd(typeof(VegetationStudioOutput), null, replace: true); }
public static new void Process(CoordRect rect, Chunk.Results results, GeneratorsAsset gens, Chunk.Size terrainSize, Func <float, bool> stop = null) { #if CTS_PRESENT if (stop != null && stop(0)) { return; } //gathering prototypes and matrices lists List <int> indexesList = new List <int>(); List <float> opacities = new List <float>(); List <Matrix> matrices = new List <Matrix>(); List <Matrix> biomeMasks = new List <Matrix>(); foreach (CTSOutput gen in gens.GeneratorsOfType <CTSOutput>(onlyEnabled: true, checkBiomes: true)) { //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]; matrix.Clamp01(); //adding to lists matrices.Add(matrix); biomeMasks.Add(gen.biome == null ? null : biomeMask); indexesList.Add(gen.baseLayers[i].index); opacities.Add(gen.baseLayers[i].opacity); } } //optimizing matrices list if they are not used //CTS always use 16 channels, so this optimization is useless (and does not work when layer order changed) //for (int i = matrices.Count - 1; i >= 0; i--) // if (opacities[i] < 0.001f || matrices[i].IsEmpty() || (biomeMasks[i] != null && biomeMasks[i].IsEmpty())) // { indexesList.RemoveAt(i); opacities.RemoveAt(i); matrices.RemoveAt(i); biomeMasks.RemoveAt(i); } //creating array float[,,] splats3D = new float[terrainSize.resolution, terrainSize.resolution, 16]; //TODO: use max index if (matrices.Count == 0) { results.apply.CheckAdd(typeof(CTSOutput), splats3D, replace: true); return; } //filling array if (stop != null && stop(0)) { return; } int numLayers = matrices.Count; int numPrototypes = splats3D.GetLength(2); int maxX = splats3D.GetLength(0); int maxZ = splats3D.GetLength(1); //MapMagic.instance.resolution should not be used because of possible lods //CoordRect rect = matrices[0].rect; float[] values = new float[numPrototypes]; //row, to avoid reading/writing 3d array (it is too slow) for (int x = 0; x < maxX; x++) { for (int z = 0; z < maxZ; z++) { int pos = rect.GetPos(x + rect.offset.x, z + rect.offset.z); float sum = 0; //clearing values for (int i = 0; i < numPrototypes; i++) { values[i] = 0; } //getting values for (int i = 0; i < numLayers; i++) { float val = matrices[i].array[pos]; if (biomeMasks[i] != null) { val *= biomeMasks[i].array[pos]; //if mask is not assigned biome was ignored, so only main outs with mask==null left here } if (val < 0) { val = 0; } if (val > 1) { val = 1; } sum += val; //normalizing: calculating sum values[indexesList[i]] += val; } //setting color for (int i = 0; i < numLayers; i++) { splats3D[z, x, i] = values[i] / sum; } } } //pushing to apply if (stop != null && stop(0)) { return; } results.apply.CheckAdd(typeof(CTSOutput), splats3D, replace: true); #endif }