private void RenderBiomeOrderEditor() { BiomeNode[] biomes = En.GetBiomes(); _biomeOrderProperty = serializedObject.FindProperty("BiomeOrder"); if (_biomeOrderProperty.arraySize == biomes.Length) { return; } _biomeOrderProperty.ClearArray(); for (int i = 0; i < biomes.Length; i++) { _biomeOrderProperty.arraySize++; _biomeOrderProperty.GetArrayElementAtIndex(i).intValue = i; } serializedObject.ApplyModifiedProperties(); _reorderableList = new ReorderableList( serializedObject, _biomeOrderProperty, true, false, false, false ) { drawElementCallback = (rect, index, active, focused) => { BiomeNode selectedBiome = En.GetBiomes()[index]; string name = String.IsNullOrEmpty(selectedBiome.Name) ? "Unnamed Biome" : selectedBiome.Name; EditorGUI.LabelField( new Rect(rect.x, rect.y, GetWidth() - 10, EditorGUIUtility.singleLineHeight), name); } }; }
public Texture2D GetPreviewTexture(int size) { BiomeNode[] nodes = _combiner.GetConnectedBiomeNodes(); Texture2D tex = new Texture2D(size, size); float[,,] map = GetBiomeMap(size); for (int x = 0; x < size; x++) { for (int y = 0; y < size; y++) { Color final = new Color(0, 0, 0); for (int z = 0; z < nodes.Length; z++) { float weight = map[x, y, z]; BiomeNode biome = nodes[z]; final += biome.PreviewColor * weight; } tex.SetPixel(x, y, final); } } tex.Apply(); return(tex); }
public BiomeNode[] GetBiomes() { BiomeNode[] biomes = GetInputValues <BiomeNode>("Biomes"); if (BlendStrategy != BlendStrategy.ORDERED || BiomeOrder.Length != biomes.Length) { return(biomes); } BiomeNode[] sorted = new BiomeNode[biomes.Length]; foreach (int i in BiomeOrder) { sorted[i] = biomes[BiomeOrder[i]]; } return(sorted); }
public override void OnBodyGUI() { //Output NodeEditorGUILayout.PropertyField(serializedObject.FindProperty("Output")); //Draw mix method enum SerializedProperty mixType = serializedObject.FindProperty("Mix"); NodeEditorGUILayout.PropertyField(mixType, new GUIContent("Mix Type")); //Draw Instance Ports with colors if (!Bcn.DidAddPort) { NodePort[] ports = Bcn.GetInstanceInputs(); for (var i = 0; i < ports.Length; i++) { NodePort p = ports[i]; EditorGUILayout.BeginHorizontal(); NodeEditorGUILayout.PortField(p, GUILayout.ExpandWidth(false)); BiomeNode node = p.GetInputValue <BiomeNode>(); if (node != null) { #if UNITY_2018_1_OR_NEWER EditorGUILayout.ColorField(GUIContent.none, node.PreviewColor, false, false, false, GUILayout.MaxWidth(32f)); #else EditorGUILayout.ColorField(GUIContent.none, node.PreviewColor, false, false, false, null, GUILayout.MaxWidth(32f)); #endif } EditorGUILayout.EndHorizontal(); } } else { Bcn.DidAddPort = false; } //Show Preview PreviewField.Show(Bcn); }
/// <summary> /// Generates a map of biomes constructed from connected biome nodes. /// </summary> /// <param name="position">Position of this biome map in the grid of tiles</param> /// <param name="length">Length of a tile</param> /// <param name="spread">Divide x & z coordinates of the polled position by this number</param> /// <param name="resolution">Resolution of map</param> /// <returns></returns> public float[,,] GetBiomeMap(GridPosition position, int length, float spread, int resolution) { BiomeNode[] connected = _combiner.GetConnectedBiomeNodes(); float[,,] biomeMap = new float[resolution, resolution, connected.Length]; List <float[, ]> weightedBiomeValues = new List <float[, ]>(biomeMap.Length); //Gather each biome's values lock (_valueLock) { for (var i = 0; i < connected.Length; i++) { BiomeNode biome = connected[i]; BiomeNode.BiomeMapResult biomeResults = biome.GetMapsValues(position, resolution, spread, length); if (_cachedMinMax == null) { _cachedMinMax = CalculateMinMax(TerraConfig.Instance.Generator.RemapResolution); } float[,] weighted = biome.GetWeightedValues(biomeResults.Values, _cachedMinMax.Value.Min, _cachedMinMax.Value.Max); weightedBiomeValues.Add(weighted); } } lock (_weightLock) { for (int x = 0; x < resolution; x++) { for (int y = 0; y < resolution; y++) { float[] map = CalculateBiomeWeightsAt(x, y, connected, weightedBiomeValues); for (int z = 0; z < map.Length; z++) { biomeMap[x, y, z] = map[z]; } } } } return(biomeMap); }
/// <summary> /// Generates a map of biomes constructed from connected biome nodes. /// </summary> /// <param name="position">Position of this biome map in the grid of tiles</param> /// <param name="length">Length of a tile</param> /// <param name="spread">Divide x & z coordinates of the polled position by this number</param> /// <param name="resolution">Resolution of map</param> /// <returns> /// Index of a biome enabled at each x, y coordinate. The index corresponds /// to the order of biomes defined in the node graph. e.g. an index of 0 /// represents the first biome. /// </returns> public int[,] GetBiomeMap(GridPosition position, int length, float spread, int resolution) { List <float[, ]> allBiomeWeights = new List <float[, ]>(_biomes.Length); //Gather each biome's weights lock (_valueLock) { for (var i = 0; i < _biomes.Length; i++) { BiomeNode biome = _biomes[i]; allBiomeWeights.Add(biome.GetWeights(position, resolution, spread, length)); } } // Choose biome to display int[,] biomeMap = new int[resolution, resolution]; BlendStrategy blendStrategy = TerraConfig.Instance.Graph.GetEndNode().BlendStrategy; MathUtil.LoopXY(resolution, (x, y) => { int toShow = 0; switch (blendStrategy) { case BlendStrategy.RANDOM: toShow = GetShownBiomeRandom(x, y, allBiomeWeights); break; case BlendStrategy.ORDERED: toShow = GetShownBiomeOrdered(x, y, allBiomeWeights); break; } biomeMap[x, y] = toShow; }); return(biomeMap); }
/// <summary> /// Adds trees according to the <see cref="_biomeMap"/> in /// this <see cref="Tile"/> /// </summary> public IEnumerator AddTrees() { if (_biomeMap == null) { yield break; } //Collect prototypes from tree nodes TreeDetailNode[] allTreeNodes = _painter.Biomes .SelectMany(biome => biome.GetTreeInputs()) .ToArray(); List <TreePrototype> prototypes = new List <TreePrototype>(allTreeNodes.Length); foreach (TreeDetailNode treeNode in allTreeNodes) { prototypes.Add((TreePrototype)treeNode.GetDetailPrototype()); } int coroutineRes = TerraConfig.Instance.Generator.CoroutineRes; int iterations = 0; GenerationData conf = TerraConfig.Instance.Generator; _terrain.terrainData.treePrototypes = prototypes.ToArray(); _terrain.terrainData.RefreshPrototypes(); _terrain.treeDistance = conf.TreeDistance; _terrain.treeBillboardDistance = conf.BillboardStart; _terrain.treeMaximumFullLODCount = conf.MaxMeshTrees; BiomeNode[] biomeNodes = _painter.Biomes; int prototypeIndex = 0; for (int i = 0; i < biomeNodes.Length; i++) { //Collect all trees for this biome BiomeNode biome = biomeNodes[i]; TreeDetailNode[] treeNodes = biome.GetTreeInputs(); if (treeNodes.Length == 0) //A biome may contain no trees { continue; } foreach (TreeDetailNode treeNode in treeNodes) { //Get map of normalized "tree positions" Vector2[] samples = null; if (!TerraConfig.Instance.IsEditor) { bool isDone = false; TreeDetailNode node = treeNode; TerraConfig.Instance.Worker.Enqueue(() => samples = node.SamplePositions(_tile.Random), () => isDone = true); while (!isDone) { yield return(null); } } else { samples = treeNode.SamplePositions(_tile.Random); } foreach (Vector2 sample in samples) { int selectedBiome = _sampler.GetBiomeAtInterpolatedCoords(_biomeMap, sample.x, sample.y); if (iterations > coroutineRes) { iterations = 0; yield return(null); } if (selectedBiome != i) { continue; //Not in this biome, skip } //Check whether a tree can be placed here float amp = TerraConfig.Instance.Generator.Amplitude; float height = _terrain.terrainData.GetInterpolatedHeight(sample.x, sample.y) / amp; float angle = Vector3.Angle(Vector3.up, _terrain.terrainData.GetInterpolatedNormal(sample.x, sample.y)) / 90; Vector2 world = MathUtil.NormalToWorld(_tile.GridPosition, sample); if (treeNode.ShouldPlaceAt(world.x, world.y, height, angle)) { //Add tree to terrain Vector3 treeLoc = new Vector3(sample.x, height, sample.y); //Tree sample set index matches the tree prototype index (j) TreeInstance tree = treeNode.GetTreeInstance(treeLoc, prototypeIndex, _tile.Random); _terrain.AddTreeInstance(tree); } } prototypeIndex++; } } }
/// <summary> /// Adds all non-tree details to the Terrain according to the /// <see cref="_biomeMap"/> in this <see cref="Tile"/>. This adds /// grass and detail meshes. /// </summary> public IEnumerator AddDetailLayers() { BiomeNode[] biomeNodes = _painter.Biomes; int res = TerraConfig.Instance.Generator.DetailmapResolution; //Collect prototypes GrassDetailNode[] allDetailNodes = biomeNodes .SelectMany(bn => bn.GetGrassInputs()) .ToArray(); List <DetailPrototype> prototypes = new List <DetailPrototype>(allDetailNodes.Length); foreach (GrassDetailNode detailNode in allDetailNodes) { prototypes.Add((DetailPrototype)detailNode.GetDetailPrototype()); } int coroutineRes = TerraConfig.Instance.Generator.CoroutineRes; int iterations = 0; int prototypeIndex = 0; GenerationData conf = TerraConfig.Instance.Generator; _terrain.terrainData.SetDetailResolution(res, 16); _terrain.terrainData.detailPrototypes = prototypes.ToArray(); _terrain.detailObjectDistance = conf.DetailDistance; _terrain.detailObjectDensity = conf.DetailDensity; for (int i = 0; i < biomeNodes.Length; i++) { //Collect all details for this biome BiomeNode biome = biomeNodes[i]; GrassDetailNode[] grassNodes = biome.GetGrassInputs(); if (grassNodes.Length == 0) //A biome may contain no grass nodes { continue; } foreach (GrassDetailNode grassNode in grassNodes) { int[,] layer = new int[res, res]; //Get map of normalized placement positions Vector2[] samples = null; if (!TerraConfig.Instance.IsEditor) { bool isDone = false; GrassDetailNode node = grassNode; TerraConfig.Instance.Worker.Enqueue(() => samples = node.SamplePositions(_tile.Random), () => isDone = true); while (!isDone) { yield return(null); } } else { samples = grassNode.SamplePositions(_tile.Random); } foreach (Vector2 sample in samples) { iterations++; if (iterations > coroutineRes) { iterations = 0; yield return(null); } int selectedBiome = _sampler.GetBiomeAtInterpolatedCoords(_biomeMap, sample.x, sample.y); if (selectedBiome != i) { continue; //Not in this biome, skip } //Check whether an object can be placed here float amp = TerraConfig.Instance.Generator.Amplitude; float height = _terrain.terrainData.GetInterpolatedHeight(sample.x, sample.y) / amp; float angle = Vector3.Angle(Vector3.up, _terrain.terrainData.GetInterpolatedNormal(sample.x, sample.y)) / 90; Vector2 world = MathUtil.NormalToWorld(_tile.GridPosition, sample); if (grassNode.ShouldPlaceAt(world.x, world.y, height, angle)) { //Convert normalized x,y coordinates to positions in layer map Vector2 sampleWorld = sample * res; int x = Mathf.Clamp(Mathf.RoundToInt(sampleWorld.x), 0, res - 1); int y = Mathf.Clamp(Mathf.RoundToInt(sampleWorld.y), 0, res - 1); layer[y, x] = 1; //Display object here } } _terrain.terrainData.SetDetailLayer(0, 0, prototypeIndex, layer); prototypeIndex++; } } }