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); }