public static void Process(MapMagic.CoordRect rect, Chunk.Results results, GeneratorsAsset gens, Chunk.Size terrainSize, Func <float, bool> stop = null) { if (stop != null && stop(0)) { return; } Noise noise = new Noise(12345, permutationCount: 128); //to pick objects based on biome List <TreeInstance> instancesList = new List <TreeInstance>(); List <TreePrototype> prototypesList = new List <TreePrototype>(); //find all of the biome masks - they will be used to determine object probability List <TupleSet <MadMapsTreeOutput, Matrix> > allGensMasks = new List <TupleSet <MadMapsTreeOutput, Matrix> >(); foreach (MadMapsTreeOutput gen in gens.GeneratorsOfType <MadMapsTreeOutput>(onlyEnabled: true, checkBiomes: true)) { 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 } } allGensMasks.Add(new TupleSet <MadMapsTreeOutput, Matrix>(gen, biomeMask)); } int allGensMasksCount = allGensMasks.Count; //biome rect to find array pos faster MapMagic.CoordRect biomeRect = new MapMagic.CoordRect(); for (int g = 0; g < allGensMasksCount; g++) { if (allGensMasks[g].item2 != null) { biomeRect = allGensMasks[g].item2.rect; break; } } //prepare biome mask values stack to re-use it to find per-coord biome float[] biomeVals = new float[allGensMasksCount]; //+1 for not using any object at all //iterating all gens for (int g = 0; g < allGensMasksCount; g++) { MadMapsTreeOutput gen = allGensMasks[g].item1; //iterating in layers for (int b = 0; b < gen.baseLayers.Length; b++) { if (stop != null && stop(0)) { return; //checking stop before reading output } Layer layer = gen.baseLayers[b]; // if (layer.prefab == null) continue; //loading objects from input SpatialHash hash = (SpatialHash)gen.baseLayers[b].input.GetObject(results); if (hash == null) { continue; } //adding prototype // if (layer.prefab == null) continue; TreePrototype prototype = new TreePrototype() { prefab = layer.prefab, bendFactor = layer.bendFactor }; prototypesList.Add(prototype); int prototypeNum = prototypesList.Count - 1; //filling instances (no need to check/add key in multidict) foreach (SpatialObject obj in hash.AllObjs()) { //blend biomes - calling continue if improper biome if (biomeBlendType == BiomeBlendType.Sharp) { float biomeVal = 1; if (allGensMasks[g].item2 != null) { biomeVal = allGensMasks[g].item2[obj.pos]; } if (biomeVal < 0.5f) { continue; } } else if (biomeBlendType == BiomeBlendType.AdditiveRandom) { float biomeVal = 1; if (allGensMasks[g].item2 != null) { biomeVal = allGensMasks[g].item2[obj.pos]; } float rnd = noise.Random((int)obj.pos.x, (int)obj.pos.y); if (biomeVal > 0.5f) { rnd = 1 - rnd; } if (biomeVal < rnd) { continue; } } else if (biomeBlendType == BiomeBlendType.NormalizedRandom) { //filling biome masks values int pos = biomeRect.GetPos(obj.pos); for (int i = 0; i < allGensMasksCount; i++) { if (allGensMasks[i].item2 != null) { biomeVals[i] = allGensMasks[i].item2.array[pos]; } else { biomeVals[i] = 1; } } //calculate normalized sum float sum = 0; for (int i = 0; i < biomeVals.Length; i++) { sum += biomeVals[i]; } if (sum > 1) //note that if sum is <1 usedBiomeNum can exceed total number of biomes - it means that none object is used here { for (int i = 0; i < biomeVals.Length; i++) { biomeVals[i] = biomeVals[i] / sum; } } //finding used biome num float rnd = noise.Random((int)obj.pos.x, (int)obj.pos.y); int usedBiomeNum = biomeVals.Length; //none biome by default sum = 0; for (int i = 0; i < biomeVals.Length; i++) { sum += biomeVals[i]; if (sum > rnd) { usedBiomeNum = i; break; } } //disable object using biome mask if (usedBiomeNum != g) { continue; } } //scale mode is applied a bit later //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; } TreeInstance tree = new TreeInstance(); tree.position = new Vector3( (obj.pos.x - hash.offset.x) / hash.size, obj.height + terrainHeight, (obj.pos.y - hash.offset.y) / hash.size); tree.rotation = layer.rotate ? obj.rotation % 360 : 0; tree.widthScale = layer.widthScale ? obj.size : 1; tree.heightScale = layer.heightScale ? obj.size : 1; tree.prototypeIndex = prototypeNum; tree.color = layer.color; tree.lightmapColor = layer.color; if (biomeBlendType == BiomeBlendType.Scale) { float biomeVal = 1; if (allGensMasks[g].item2 != null) { biomeVal = allGensMasks[g].item2[obj.pos]; } if (biomeVal < 0.001f) { continue; } tree.widthScale *= biomeVal; tree.heightScale *= biomeVal; } instancesList.Add(tree); } } } //setting output if (stop != null && stop(0)) { return; } if (instancesList.Count == 0 && prototypesList.Count == 0) { return; //empty, process is caused by height change } TupleSet <TreeInstance[], TreePrototype[]> treesTuple = new TupleSet <TreeInstance[], TreePrototype[]>(instancesList.ToArray(), prototypesList.ToArray()); results.apply.CheckAdd(typeof(MadMapsTreeOutput), treesTuple, replace: true); }
public static void Process(MapMagic.CoordRect rect, Chunk.Results results, GeneratorsAsset gens, Chunk.Size terrainSize, Func <float, bool> stop = null) { if (stop != null && stop(0)) { return; } Noise noise = new Noise(12345, permutationCount: 128); //to pick objects based on biome //preparing output Dictionary <Transform, List <ObjectPool.Transition> > transitions = new Dictionary <Transform, List <ObjectPool.Transition> >(); //find all of the biome masks - they will be used to determine object probability List <TupleSet <MadMapsObjectOutput, Matrix> > allGensMasks = new List <TupleSet <MadMapsObjectOutput, Matrix> >(); foreach (MadMapsObjectOutput gen in gens.GeneratorsOfType <MadMapsObjectOutput>(onlyEnabled: true, checkBiomes: true)) { 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 } } allGensMasks.Add(new TupleSet <MadMapsObjectOutput, Matrix>(gen, biomeMask)); } int allGensMasksCount = allGensMasks.Count; //biome rect to find array pos faster MapMagic.CoordRect biomeRect = new MapMagic.CoordRect(); for (int g = 0; g < allGensMasksCount; g++) { if (allGensMasks[g].item2 != null) { biomeRect = allGensMasks[g].item2.rect; break; } } //prepare biome mask values stack to re-use it to find per-coord biome float[] biomeVals = new float[allGensMasksCount]; //+1 for not using any object at all //iterating all gens for (int g = 0; g < allGensMasksCount; g++) { MadMapsObjectOutput gen = allGensMasks[g].item1; //iterating in layers for (int b = 0; b < gen.baseLayers.Length; b++) { if (stop != null && stop(0)) { return; //checking stop before reading output } Layer layer = gen.baseLayers[b]; if (!layer.prefab) { continue; } //loading objects from input SpatialHash hash = (SpatialHash)gen.baseLayers[b].input.GetObject(results); if (hash == null) { continue; } //finding/creating proper transitions list List <ObjectPool.Transition> transitionsList; if (!transitions.ContainsKey(layer.prefab)) { transitionsList = new List <ObjectPool.Transition>(); transitions.Add(layer.prefab, transitionsList); } else { transitionsList = transitions[layer.prefab]; } //filling instances (no need to check/add key in multidict) foreach (SpatialObject obj in hash.AllObjs()) { //blend biomes - calling continue if improper biome if (biomeBlendType == BiomeBlendType.Sharp) { float biomeVal = 1; if (allGensMasks[g].item2 != null) { biomeVal = allGensMasks[g].item2[obj.pos]; } if (biomeVal < 0.5f) { continue; } } else if (biomeBlendType == BiomeBlendType.AdditiveRandom) { float biomeVal = 1; if (allGensMasks[g].item2 != null) { biomeVal = allGensMasks[g].item2[obj.pos]; } float rnd = noise.Random((int)obj.pos.x, (int)obj.pos.y); if (biomeVal > 0.5f) { rnd = 1 - rnd; } if (biomeVal < rnd) { continue; } } else if (biomeBlendType == BiomeBlendType.NormalizedRandom) { //filling biome masks values int pos = biomeRect.GetPos(obj.pos); for (int i = 0; i < allGensMasksCount; i++) { if (allGensMasks[i].item2 != null) { biomeVals[i] = allGensMasks[i].item2.array[pos]; } else { biomeVals[i] = 1; } } //calculate normalized sum float sum = 0; for (int i = 0; i < biomeVals.Length; i++) { sum += biomeVals[i]; } if (sum > 1) //note that if sum is <1 usedBiomeNum can exceed total number of biomes - it means that none object is used here { for (int i = 0; i < biomeVals.Length; i++) { biomeVals[i] = biomeVals[i] / sum; } } //finding used biome num float rnd = noise.Random((int)obj.pos.x, (int)obj.pos.y); int usedBiomeNum = biomeVals.Length; //none biome by default sum = 0; for (int i = 0; i < biomeVals.Length; i++) { sum += biomeVals[i]; if (sum > rnd) { usedBiomeNum = i; break; } } //disable object using biome mask if (usedBiomeNum != g) { continue; } } //scale mode is applied a bit later //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; //world-space object position Vector3 position = new Vector3( (obj.pos.x) / hash.size * terrainSize.dimensions, // relative (0-1) position * terrain dimension (obj.height + terrainHeight) * terrainSize.height, (obj.pos.y) / hash.size * terrainSize.dimensions); //rotation + taking terrain normal Quaternion rotation; float objRotation = layer.rotate ? obj.rotation % 360 : 0; if (layer.takeTerrainNormal) { Vector3 terrainNormal = 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 == BiomeBlendType.Scale) { float biomeVal = 1; if (allGensMasks[g].item2 != null) { biomeVal = allGensMasks[g].item2[obj.pos]; } if (biomeVal < 0.001f) { continue; } scale *= biomeVal; } transitionsList.Add(new ObjectPool.Transition() { pos = position, rotation = rotation, scale = scale }); } } } //queue apply if (stop != null && stop(0)) { return; } results.apply.CheckAdd(typeof(MadMapsObjectOutput), transitions, replace: true); }
public 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; } Noise noise = new Noise(12345, permutationCount: 128); //to pick objects based on biome if (stop != null && stop(0)) { return; } List <VegetationStudioInstance> instances = new List <VegetationStudioInstance>(); //object outputs foreach (MadMapsVSOutput gen in gens.GeneratorsOfType <MadMapsVSOutput>(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, (obj.height + terrainHeight) * terrainSize.height, (obj.pos.y - hash.offset.y) / hash.size); //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; } instances.Add(new VegetationStudioInstance() { VSID = id, Guid = System.Guid.NewGuid().ToString(), Package = package, Position = position, Scale = scale, Rotation = rotation.eulerAngles, }); } if (stop != null && stop(0)) { return; } } int cellXCount = Mathf.CeilToInt(terrainSize.dimensions / cellSize); int cellZCount = Mathf.CeilToInt(terrainSize.dimensions / cellSize); //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); var position = new Vector3(mx, 0, mz); instances.Add(new VegetationStudioInstance() { VSID = id, Guid = System.Guid.NewGuid().ToString(), Package = package, Position = position, Scale = scale, Rotation = rotation.eulerAngles, }); } } } 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(MadMapsVSOutput), instances, replace: true); }