private ulong ParsePackageId(TreeInstance treeInstance) { ulong result = 0; var match = Regex.Match(treeInstance.Info.name, RegexExpression.PackageId); if (match.Success && !string.IsNullOrEmpty(match.Value)) result = ulong.Parse(match.Value); return result; }
void copyTerrain(Terrain origTerrain, string newName, float xMin, float xMax, float zMin, float zMax, int heightmapResolution, int detailResolution, int alphamapResolution) { if (heightmapResolution < 33 || heightmapResolution > 4097) { Debug.Log("Invalid heightmapResolution " + heightmapResolution); return; } if (detailResolution < 0 || detailResolution > 4048) { Debug.Log("Invalid detailResolution " + detailResolution); return; } if (alphamapResolution < 16 || alphamapResolution > 2048) { Debug.Log("Invalid alphamapResolution " + alphamapResolution); return; } if (xMin < 0 || xMin > xMax || xMax > origTerrain.terrainData.size.x) { Debug.Log("Invalid xMin or xMax"); return; } if (zMin < 0 || zMin > zMax || zMax > origTerrain.terrainData.size.z) { Debug.Log("Invalid zMin or zMax"); return; } if (AssetDatabase.FindAssets(newName).Length != 0) { Debug.Log("Asset with name " + newName + " already exists"); return; } TerrainData td = new TerrainData(); GameObject gameObject = Terrain.CreateTerrainGameObject(td); Terrain newTerrain = gameObject.GetComponent <Terrain>(); if (!AssetDatabase.IsValidFolder("Assets/Resources")) { AssetDatabase.CreateFolder("Assets", "Resources"); } // Must do this before Splat AssetDatabase.CreateAsset(td, "Assets/Resources/" + newName + ".asset"); // Copy over all vars newTerrain.bakeLightProbesForTrees = origTerrain.bakeLightProbesForTrees; newTerrain.basemapDistance = origTerrain.basemapDistance; newTerrain.castShadows = origTerrain.castShadows; newTerrain.collectDetailPatches = origTerrain.collectDetailPatches; newTerrain.detailObjectDensity = origTerrain.detailObjectDensity; newTerrain.detailObjectDistance = origTerrain.detailObjectDistance; newTerrain.drawHeightmap = origTerrain.drawHeightmap; newTerrain.drawTreesAndFoliage = origTerrain.drawTreesAndFoliage; newTerrain.editorRenderFlags = origTerrain.editorRenderFlags; newTerrain.heightmapMaximumLOD = origTerrain.heightmapMaximumLOD; newTerrain.heightmapPixelError = origTerrain.heightmapPixelError; newTerrain.legacyShininess = origTerrain.legacyShininess; newTerrain.legacySpecular = origTerrain.legacySpecular; newTerrain.lightmapIndex = origTerrain.lightmapIndex; newTerrain.lightmapScaleOffset = origTerrain.lightmapScaleOffset; newTerrain.materialTemplate = origTerrain.materialTemplate; newTerrain.materialType = origTerrain.materialType; newTerrain.realtimeLightmapIndex = origTerrain.realtimeLightmapIndex; newTerrain.realtimeLightmapScaleOffset = origTerrain.realtimeLightmapScaleOffset; newTerrain.reflectionProbeUsage = origTerrain.reflectionProbeUsage; newTerrain.treeBillboardDistance = origTerrain.treeBillboardDistance; newTerrain.treeCrossFadeLength = origTerrain.treeCrossFadeLength; newTerrain.treeDistance = origTerrain.treeDistance; newTerrain.treeMaximumFullLODCount = origTerrain.treeMaximumFullLODCount; td.splatPrototypes = origTerrain.terrainData.splatPrototypes; td.treePrototypes = origTerrain.terrainData.treePrototypes; td.detailPrototypes = origTerrain.terrainData.detailPrototypes; // Get percent of original float xMinNorm = xMin / origTerrain.terrainData.size.x; float xMaxNorm = xMax / origTerrain.terrainData.size.x; float zMinNorm = zMin / origTerrain.terrainData.size.z; float zMaxNorm = zMax / origTerrain.terrainData.size.z; float dimRatio1, dimRatio2; // Height td.heightmapResolution = heightmapResolution; float[,] newHeights = new float[heightmapResolution, heightmapResolution]; dimRatio1 = (xMax - xMin) / heightmapResolution; dimRatio2 = (zMax - zMin) / heightmapResolution; for (int i = 0; i < heightmapResolution; i++) { for (int j = 0; j < heightmapResolution; j++) { // Divide by size.y because height is stored as percentage // Note this is [j, i] and not [i, j] (Why?!) newHeights[j, i] = origTerrain.SampleHeight(new Vector3(xMin + (i * dimRatio1), 0, zMin + (j * dimRatio2))) / origTerrain.terrainData.size.y; } } td.SetHeightsDelayLOD(0, 0, newHeights); // Detail td.SetDetailResolution(detailResolution, 8); // Default? Haven't messed with resolutionPerPatch for (int layer = 0; layer < origTerrain.terrainData.detailPrototypes.Length; layer++) { int[,] detailLayer = origTerrain.terrainData.GetDetailLayer( Mathf.FloorToInt(xMinNorm * origTerrain.terrainData.detailWidth), Mathf.FloorToInt(zMinNorm * origTerrain.terrainData.detailHeight), Mathf.FloorToInt((xMaxNorm - xMinNorm) * origTerrain.terrainData.detailWidth), Mathf.FloorToInt((zMaxNorm - zMinNorm) * origTerrain.terrainData.detailHeight), layer); int[,] newDetailLayer = new int[detailResolution, detailResolution]; dimRatio1 = (float)detailLayer.GetLength(0) / detailResolution; dimRatio2 = (float)detailLayer.GetLength(1) / detailResolution; for (int i = 0; i < newDetailLayer.GetLength(0); i++) { for (int j = 0; j < newDetailLayer.GetLength(1); j++) { newDetailLayer[i, j] = detailLayer[Mathf.FloorToInt(i * dimRatio1), Mathf.FloorToInt(j * dimRatio2)]; } } td.SetDetailLayer(0, 0, layer, newDetailLayer); } // Splat td.alphamapResolution = alphamapResolution; float[,,] alphamaps = origTerrain.terrainData.GetAlphamaps( Mathf.FloorToInt(xMinNorm * origTerrain.terrainData.alphamapWidth), Mathf.FloorToInt(zMinNorm * origTerrain.terrainData.alphamapHeight), Mathf.FloorToInt((xMaxNorm - xMinNorm) * origTerrain.terrainData.alphamapWidth), Mathf.FloorToInt((zMaxNorm - zMinNorm) * origTerrain.terrainData.alphamapHeight)); // Last dim is always origTerrain.terrainData.splatPrototypes.Length so don't ratio float[,,] newAlphamaps = new float[alphamapResolution, alphamapResolution, alphamaps.GetLength(2)]; dimRatio1 = (float)alphamaps.GetLength(0) / alphamapResolution; dimRatio2 = (float)alphamaps.GetLength(1) / alphamapResolution; for (int i = 0; i < newAlphamaps.GetLength(0); i++) { for (int j = 0; j < newAlphamaps.GetLength(1); j++) { for (int k = 0; k < newAlphamaps.GetLength(2); k++) { newAlphamaps[i, j, k] = alphamaps[Mathf.FloorToInt(i * dimRatio1), Mathf.FloorToInt(j * dimRatio2), k]; } } } td.SetAlphamaps(0, 0, newAlphamaps); // Tree for (int i = 0; i < origTerrain.terrainData.treeInstanceCount; i++) { TreeInstance ti = origTerrain.terrainData.treeInstances[i]; if (ti.position.x < xMinNorm || ti.position.x >= xMaxNorm) { continue; } if (ti.position.z < zMinNorm || ti.position.z >= zMaxNorm) { continue; } ti.position = new Vector3(((ti.position.x * origTerrain.terrainData.size.x) - xMin) / (xMax - xMin), ti.position.y, ((ti.position.z * origTerrain.terrainData.size.z) - zMin) / (zMax - zMin)); newTerrain.AddTreeInstance(ti); } gameObject.transform.position = new Vector3(origTerrain.transform.position.x + xMin, origTerrain.transform.position.y, origTerrain.transform.position.z + zMin); gameObject.name = newName; // Must happen after setting heightmapResolution td.size = new Vector3(xMax - xMin, origTerrain.terrainData.size.y, zMax - zMin); AssetDatabase.SaveAssets(); }
public void AddTreeInstance(TreeInstance instance){}
public void PopulateGroupData( ushort segmentID, uint laneID, NetInfo.Lane laneInfo, bool destroyed, NetNode.Flags startFlags, NetNode.Flags endFlags, float startAngle, float endAngle, bool invert, bool terrainHeight, int layer, ref int vertexIndex, ref int triangleIndex, Vector3 groupPosition, RenderGroup.MeshData data, ref Vector3 min, ref Vector3 max, ref float maxRenderDistance, ref float maxInstanceDistance, ref bool hasProps) { NetLaneProps laneProps = laneInfo.m_laneProps; if (laneProps?.m_props == null) { return; } bool backward = (laneInfo.m_finalDirection & NetInfo.Direction.Both) == NetInfo.Direction.Backward || (laneInfo.m_finalDirection & NetInfo.Direction.AvoidBoth) == NetInfo.Direction.AvoidForward; bool reverse = backward != invert; if (backward) //swap { NetNode.Flags flags = startFlags; startFlags = endFlags; endFlags = flags; } int nProps = laneProps.m_props.Length; for (int i = 0; i < nProps; i++) { NetLaneProps.Prop prop = laneProps.m_props[i]; if (!prop.CheckFlags(m_flags, startFlags, endFlags) || m_length < prop.m_minLength) { continue; } int repeatCountTimes2 = 2; if (prop.m_repeatDistance > 1f) { repeatCountTimes2 *= Mathf.Max(1, Mathf.RoundToInt(m_length / prop.m_repeatDistance)); } float halfSegmentOffset = prop.m_segmentOffset * 0.5f; if (m_length != 0f) { halfSegmentOffset = Mathf.Clamp(halfSegmentOffset + prop.m_position.z / m_length, -0.5f, 0.5f); } if (reverse) { halfSegmentOffset = 0f - halfSegmentOffset; } PropInfo finalProp = prop.m_finalProp; if ((object)finalProp != null) { hasProps = true; if (finalProp.m_prefabDataLayer == layer || finalProp.m_effectLayer == layer) { Color color = Color.white; Randomizer r = new Randomizer((int)laneID + i); for (int j = 1; j <= repeatCountTimes2; j += 2) { if (r.Int32(100u) >= prop.m_probability) { continue; } float t = halfSegmentOffset + (float)j / (float)repeatCountTimes2; PropInfo variation = finalProp.GetVariation(ref r); float scale = variation.m_minScale + (float)r.Int32(10000u) * (variation.m_maxScale - variation.m_minScale) * 0.0001f; if (prop.m_colorMode == NetLaneProps.ColorMode.Default) { color = variation.GetColor(ref r); } if (!variation.m_isDecal && destroyed) { continue; } Vector3 pos = m_bezier.Position(t); Vector3 tan = m_bezier.Tangent(t); if (!(tan != Vector3.zero)) { continue; } if (reverse) { tan = -tan; } tan.y = 0f; if (prop.m_position.x != 0f) { tan = Vector3.Normalize(tan); pos.x += tan.z * prop.m_position.x; pos.z -= tan.x * prop.m_position.x; } float normalAngle = Mathf.Atan2(tan.x, 0f - tan.z); if (prop.m_cornerAngle != 0f || prop.m_position.x != 0f) { float angleDiff = endAngle - startAngle; if (angleDiff > Mathf.PI) { angleDiff -= Mathf.PI * 2f; } if (angleDiff < -Mathf.PI) { angleDiff += Mathf.PI * 2f; } var angle2 = startAngle + angleDiff * t - normalAngle; if (angle2 > Mathf.PI) { angle2 -= Mathf.PI * 2f; } if (angle2 < -Mathf.PI) { angle2 += Mathf.PI * 2f; } normalAngle += angle2 * prop.m_cornerAngle; if (angle2 != 0f && prop.m_position.x != 0f) { float d = Mathf.Tan(angle2); pos.x += tan.x * d * prop.m_position.x; pos.z += tan.z * d * prop.m_position.x; } } if (terrainHeight) { if (variation.m_requireWaterMap) { pos.y = Singleton <TerrainManager> .instance.SampleRawHeightSmoothWithWater(pos, timeLerp : false, 0f); } else { pos.y = Singleton <TerrainManager> .instance.SampleDetailHeight(pos); } } pos.y += prop.m_position.y; InstanceID id = default(InstanceID); id.NetSegment = segmentID; PropInstance.PopulateGroupData(angle: normalAngle + prop.m_angle * (Mathf.PI / 180f), info: variation, layer: layer, id: id, position: pos, scale: scale, color: color, vertexIndex: ref vertexIndex, triangleIndex: ref triangleIndex, groupPosition: groupPosition, data: data, min: ref min, max: ref max, maxRenderDistance: ref maxRenderDistance, maxInstanceDistance: ref maxInstanceDistance); } } } if (destroyed) { continue; } TreeInfo finalTree = prop.m_finalTree; if ((object)finalTree == null) { continue; } hasProps = true; if (finalTree.m_prefabDataLayer != layer) { continue; } Randomizer r2 = new Randomizer((int)laneID + i); for (int k = 1; k <= repeatCountTimes2; k += 2) { if (r2.Int32(100u) >= prop.m_probability) { continue; } float t = halfSegmentOffset + (float)k / (float)repeatCountTimes2; TreeInfo variation2 = finalTree.GetVariation(ref r2); float scale2 = variation2.m_minScale + (float)r2.Int32(10000u) * (variation2.m_maxScale - variation2.m_minScale) * 0.0001f; float brightness = variation2.m_minBrightness + (float)r2.Int32(10000u) * (variation2.m_maxBrightness - variation2.m_minBrightness) * 0.0001f; Vector3 vector3 = m_bezier.Position(t); if (prop.m_position.x != 0f) { Vector3 vector4 = m_bezier.Tangent(t); if (reverse) { vector4 = -vector4; } vector4.y = 0f; vector4 = Vector3.Normalize(vector4); vector3.x += vector4.z * prop.m_position.x; vector3.z -= vector4.x * prop.m_position.x; } if (terrainHeight) { vector3.y = Singleton <TerrainManager> .instance.SampleDetailHeight(vector3); } vector3.y += prop.m_position.y; TreeInstance.PopulateGroupData(variation2, vector3, scale2, brightness, RenderManager.DefaultColorLocation, ref vertexIndex, ref triangleIndex, groupPosition, data, ref min, ref max, ref maxRenderDistance, ref maxInstanceDistance); } } }
private void TraverseColliders(GameObject gameObject, List <ColliderShapePair> colliderList, GameObject rootObject, NewtonBody body) { // Don't fetch colliders from children with NewtonBodies if ((gameObject == rootObject) || (gameObject.GetComponent <NewtonBody>() == null)) { //Fetch all colliders foreach (NewtonCollider collider in gameObject.GetComponents <NewtonCollider>()) { dNewtonCollision shape = collider.CreateBodyShape(body.m_world); if (shape != null) { ColliderShapePair pair; pair.m_collider = collider; pair.m_shape = shape; colliderList.Add(pair); } } Terrain terrain = gameObject.GetComponent <Terrain>(); if (terrain) { NewtonHeighfieldCollider heighfield = gameObject.GetComponent <NewtonHeighfieldCollider>(); if (heighfield) { TerrainData data = terrain.terrainData; int treesCount = data.treeInstanceCount; TreeInstance[] treeInstanceArray = data.treeInstances; TreePrototype[] treeProtoArray = data.treePrototypes; Vector3 posit = Vector3.zero; for (int i = 0; i < treesCount; i++) { TreeInstance tree = treeInstanceArray[i]; posit.x = tree.position.x * data.size.x; posit.y = tree.position.y * data.size.y; posit.z = tree.position.z * data.size.z; //Debug.Log("xxx0 " + posit); TreePrototype treeProto = treeProtoArray[tree.prototypeIndex]; GameObject treeGameObject = treeProto.prefab; foreach (NewtonCollider treeCollider in treeGameObject.GetComponents <NewtonCollider>()) { dNewtonCollision treeShape = treeCollider.CreateBodyShape(body.m_world); if (treeShape != null) { ColliderShapePair pair; Vector3 treePosit = terrain.transform.position + treeCollider.m_posit + posit; //Debug.Log("xxx1 " + treePosit); dMatrix matrix = Utils.ToMatrix(treePosit, Quaternion.identity); treeShape.SetMatrix(matrix); pair.m_collider = treeCollider; pair.m_shape = treeShape; colliderList.Add(pair); } } } } } foreach (Transform child in gameObject.transform) { TraverseColliders(child.gameObject, colliderList, rootObject, body); } } }
void SplitIt() { if (Selection.activeGameObject == null) { Debug.LogWarning("No terrain was selected"); return; } var objects = Selection.objects; var currentObjectIndex = 0; foreach (var obj in objects) { currentObjectIndex++; var tObj = obj as GameObject; if (tObj == null) { continue; } parentTerrain = tObj.GetComponent <Terrain>() as Terrain; if (parentTerrain == null) { Debug.LogWarning("Current selection is not a terrain"); return; } var progressCaption = "Spliting terrain " + parentTerrain.name + " (" + currentObjectIndex.ToString() + " of " + objects.Length.ToString() + ")"; //Split terrain for (int i = 0; i < terrainsCount; i++) { EditorUtility.DisplayProgressBar(progressCaption, "Process " + i, (float)i / terrainsCount); TerrainData td = new TerrainData(); GameObject tgo = Terrain.CreateTerrainGameObject(td); tgo.name = parentTerrain.name + " " + i; terrainData.Add(td); terrainGo.Add(tgo); Terrain genTer = tgo.GetComponent(typeof(Terrain)) as Terrain; genTer.terrainData = td; AssetDatabase.CreateAsset(td, "Assets/" + genTer.name + ".asset"); // Assign splatmaps genTer.terrainData.splatPrototypes = parentTerrain.terrainData.splatPrototypes; // Assign detail prototypes genTer.terrainData.detailPrototypes = parentTerrain.terrainData.detailPrototypes; // Assign tree information genTer.terrainData.treePrototypes = parentTerrain.terrainData.treePrototypes; // Copy parent terrain propeties #region parent properties genTer.basemapDistance = parentTerrain.basemapDistance; genTer.castShadows = parentTerrain.castShadows; genTer.detailObjectDensity = parentTerrain.detailObjectDensity; genTer.detailObjectDistance = parentTerrain.detailObjectDistance; genTer.heightmapMaximumLOD = parentTerrain.heightmapMaximumLOD; genTer.heightmapPixelError = parentTerrain.heightmapPixelError; genTer.treeBillboardDistance = parentTerrain.treeBillboardDistance; genTer.treeCrossFadeLength = parentTerrain.treeCrossFadeLength; genTer.treeDistance = parentTerrain.treeDistance; genTer.treeMaximumFullLODCount = parentTerrain.treeMaximumFullLODCount; #endregion //Start processing it // Translate peace to position #region translate peace to right position Vector3 parentPosition = parentTerrain.GetPosition(); int terraPeaces = (int)Mathf.Sqrt(terrainsCount); float spaceShiftX = parentTerrain.terrainData.size.z / terraPeaces; float spaceShiftY = parentTerrain.terrainData.size.x / terraPeaces; float xWShift = (i % terraPeaces) * spaceShiftX; float zWShift = (i / terraPeaces) * spaceShiftY; tgo.transform.position = new Vector3(tgo.transform.position.x + zWShift, tgo.transform.position.y, tgo.transform.position.z + xWShift); // Shift last position tgo.transform.position = new Vector3(tgo.transform.position.x + parentPosition.x, tgo.transform.position.y + parentPosition.y, tgo.transform.position.z + parentPosition.z ); #endregion // Split height #region split height Debug.Log("Split height"); //Copy heightmap td.heightmapResolution = parentTerrain.terrainData.heightmapResolution / terraPeaces; //Keep y same td.size = new Vector3(parentTerrain.terrainData.size.x / terraPeaces, parentTerrain.terrainData.size.y, parentTerrain.terrainData.size.z / terraPeaces ); float[,] parentHeight = parentTerrain.terrainData.GetHeights(0, 0, parentTerrain.terrainData.heightmapResolution, parentTerrain.terrainData.heightmapResolution); float[,] peaceHeight = new float[parentTerrain.terrainData.heightmapResolution / terraPeaces + 1, parentTerrain.terrainData.heightmapResolution / terraPeaces + 1 ]; // Shift calc int heightShift = parentTerrain.terrainData.heightmapResolution / terraPeaces; int startX = 0; int startY = 0; int endX = 0; int endY = 0; if (i == 0) { startX = startY = 0; endX = endY = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1; } if (i == 1) { startX = startY = 0; endX = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1; endY = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1; } if (i == 2) { startX = startY = 0; endX = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1; endY = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1; } if (i == 3) { startX = startY = 0; endX = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1; endY = parentTerrain.terrainData.heightmapResolution / terraPeaces + 1; } // iterate for (int x = startX; x < endX; x++) { EditorUtility.DisplayProgressBar(progressCaption, "Split height", (float)x / (endX - startX)); for (int y = startY; y < endY; y++) { int xShift = 0; int yShift = 0; // if (i == 0) { xShift = 0; yShift = 0; } // if (i == 1) { xShift = heightShift; yShift = 0; } // if (i == 2) { xShift = 0; yShift = heightShift; } if (i == 3) { xShift = heightShift; yShift = heightShift; } float ph = parentHeight[x + xShift, y + yShift]; peaceHeight[x, y] = ph; } } EditorUtility.ClearProgressBar(); // Set heightmap to child genTer.terrainData.SetHeights(0, 0, peaceHeight); #endregion // Split splat map #region split splat map td.alphamapResolution = parentTerrain.terrainData.alphamapResolution / terraPeaces; float[,,] parentSplat = parentTerrain.terrainData.GetAlphamaps(0, 0, parentTerrain.terrainData.alphamapResolution, parentTerrain.terrainData.alphamapResolution); float[,,] peaceSplat = new float[parentTerrain.terrainData.alphamapResolution / terraPeaces, parentTerrain.terrainData.alphamapResolution / terraPeaces, parentTerrain.terrainData.alphamapLayers ]; // Shift calc int splatShift = parentTerrain.terrainData.alphamapResolution / terraPeaces; if (i == 0) { startX = startY = 0; endX = endY = parentTerrain.terrainData.alphamapResolution / terraPeaces; } if (i == 1) { startX = startY = 0; endX = parentTerrain.terrainData.alphamapResolution / terraPeaces; endY = parentTerrain.terrainData.alphamapResolution / terraPeaces; } if (i == 2) { startX = startY = 0; endX = parentTerrain.terrainData.alphamapResolution / terraPeaces; endY = parentTerrain.terrainData.alphamapResolution / terraPeaces; } if (i == 3) { startX = startY = 0; endX = parentTerrain.terrainData.alphamapResolution / terraPeaces; endY = parentTerrain.terrainData.alphamapResolution / terraPeaces; } // iterate for (int s = 0; s < parentTerrain.terrainData.alphamapLayers; s++) { for (int x = startX; x < endX; x++) { EditorUtility.DisplayProgressBar(progressCaption, "Processing splat...", (float)x / (endX - startX)); for (int y = startY; y < endY; y++) { int xShift = 0; int yShift = 0; // if (i == 0) { xShift = 0; yShift = 0; } // if (i == 1) { xShift = splatShift; yShift = 0; } // if (i == 2) { xShift = 0; yShift = splatShift; } if (i == 3) { xShift = splatShift; yShift = splatShift; } float ph = parentSplat[x + xShift, y + yShift, s]; peaceSplat[x, y, s] = ph; } } } EditorUtility.ClearProgressBar(); // Set heightmap to child genTer.terrainData.SetAlphamaps(0, 0, peaceSplat); #endregion // Split detail map #region split detail map td.SetDetailResolution(parentTerrain.terrainData.detailResolution / terraPeaces, 8); for (int detLay = 0; detLay < parentTerrain.terrainData.detailPrototypes.Length; detLay++) { int[,] parentDetail = parentTerrain.terrainData.GetDetailLayer(0, 0, parentTerrain.terrainData.detailResolution, parentTerrain.terrainData.detailResolution, detLay); int[,] peaceDetail = new int[parentTerrain.terrainData.detailResolution / terraPeaces, parentTerrain.terrainData.detailResolution / terraPeaces ]; // Shift calc int detailShift = parentTerrain.terrainData.detailResolution / terraPeaces; if (i == 0) { startX = startY = 0; endX = endY = parentTerrain.terrainData.detailResolution / terraPeaces; } if (i == 1) { startX = startY = 0; endX = parentTerrain.terrainData.detailResolution / terraPeaces; endY = parentTerrain.terrainData.detailResolution / terraPeaces; } if (i == 2) { startX = startY = 0; endX = parentTerrain.terrainData.detailResolution / terraPeaces; endY = parentTerrain.terrainData.detailResolution / terraPeaces; } if (i == 3) { startX = startY = 0; endX = parentTerrain.terrainData.detailResolution / terraPeaces; endY = parentTerrain.terrainData.detailResolution / terraPeaces; } // iterate for (int x = startX; x < endX; x++) { EditorUtility.DisplayProgressBar(progressCaption, "Processing detail...", (float)x / (endX - startX)); for (int y = startY; y < endY; y++) { int xShift = 0; int yShift = 0; // if (i == 0) { xShift = 0; yShift = 0; } // if (i == 1) { xShift = detailShift; yShift = 0; } // if (i == 2) { xShift = 0; yShift = detailShift; } if (i == 3) { xShift = detailShift; yShift = detailShift; } int ph = parentDetail[x + xShift, y + yShift]; peaceDetail[x, y] = ph; } } EditorUtility.ClearProgressBar(); // Set heightmap to child genTer.terrainData.SetDetailLayer(0, 0, detLay, peaceDetail); } #endregion // Split tree data #region split tree data for (int t = 0; t < parentTerrain.terrainData.treeInstances.Length; t++) { EditorUtility.DisplayProgressBar(progressCaption, "Processing trees...", (float)t / parentTerrain.terrainData.treeInstances.Length); // Get tree instance TreeInstance ti = parentTerrain.terrainData.treeInstances[t]; // First section if (i == 0 && ti.position.x > 0f && ti.position.x < 0.5f && ti.position.z > 0f && ti.position.z < 0.5f ) { // Recalculate new tree position ti.position = new Vector3(ti.position.x * 2f, ti.position.y, ti.position.z * 2f); // Add tree instance genTer.AddTreeInstance(ti); } // Second section if (i == 1 && ti.position.x > 0.0f && ti.position.x < 0.5f && ti.position.z >= 0.5f && ti.position.z <= 1.0f ) { // Recalculate new tree position ti.position = new Vector3((ti.position.x) * 2f, ti.position.y, (ti.position.z - 0.5f) * 2f); // Add tree instance genTer.AddTreeInstance(ti); } // Third section if (i == 2 && ti.position.x >= 0.5f && ti.position.x <= 1.0f && ti.position.z > 0.0f && ti.position.z < 0.5f ) { // Recalculate new tree position ti.position = new Vector3((ti.position.x - 0.5f) * 2f, ti.position.y, (ti.position.z) * 2f); // Add tree instance genTer.AddTreeInstance(ti); } // Fourth section if (i == 3 && ti.position.x >= 0.5f && ti.position.x <= 1.0f && ti.position.z >= 0.5f && ti.position.z <= 1.0f ) { // Recalculate new tree position ti.position = new Vector3((ti.position.x - 0.5f) * 2f, ti.position.y, (ti.position.z - 0.5f) * 2f); // Add tree instance genTer.AddTreeInstance(ti); } } #endregion AssetDatabase.SaveAssets(); } EditorUtility.ClearProgressBar(); } }
public GameObject CloneTerrain(Terrain sourceTerrain, TerrainData workData, string desiredObjectName) { // READONLY: TODO: Heightmap Width // READONLY: TODO: Heightmap Height // Heightmap Resolution workData.heightmapResolution = sourceTerrain.terrainData.heightmapResolution; // Heightmap Scale TODO: READ ONLY :( // workData.heightmapScale = sourceTerrain.terrainData.heightmapScale; // Size workData.size = sourceTerrain.terrainData.size; // Waving Grass Strength workData.wavingGrassStrength = sourceTerrain.terrainData.wavingGrassStrength; // Waving Grass Amount workData.wavingGrassAmount = sourceTerrain.terrainData.wavingGrassAmount; // Waving Grass Speed workData.wavingGrassSpeed = sourceTerrain.terrainData.wavingGrassSpeed; // Waving Grass Tint workData.wavingGrassTint = sourceTerrain.terrainData.wavingGrassTint; // Detail Width TODO: READ ONLY :( // workData.detailWidth = sourceTerrain.terrainData.detailWidth; // Detail Height TODO: READ ONLY :( // workData.detailHeight = sourceTerrain.terrainData.detailHeight; // Detail Prototypes = DetailPrototype[] DetailPrototype[] workPrototypes = new DetailPrototype[sourceTerrain.terrainData.detailPrototypes.Length]; for (int dp = 0; dp < workPrototypes.Length; dp++) { DetailPrototype clonedPrototype = new DetailPrototype(); // prototype clonedPrototype.prototype = sourceTerrain.terrainData.detailPrototypes[dp].prototype; // prototypeTexture clonedPrototype.prototypeTexture = sourceTerrain.terrainData.detailPrototypes[dp].prototypeTexture; // minWidth clonedPrototype.minWidth = sourceTerrain.terrainData.detailPrototypes[dp].minWidth; // maxWidth clonedPrototype.maxWidth = sourceTerrain.terrainData.detailPrototypes[dp].maxWidth; // minHeight clonedPrototype.minHeight = sourceTerrain.terrainData.detailPrototypes[dp].minHeight; // maxHeight clonedPrototype.maxHeight = sourceTerrain.terrainData.detailPrototypes[dp].maxHeight; // noiseSpread clonedPrototype.noiseSpread = sourceTerrain.terrainData.detailPrototypes[dp].noiseSpread; // bendFactor clonedPrototype.bendFactor = sourceTerrain.terrainData.detailPrototypes[dp].bendFactor; // healthyColor clonedPrototype.healthyColor = sourceTerrain.terrainData.detailPrototypes[dp].healthyColor; // dryColor clonedPrototype.dryColor = sourceTerrain.terrainData.detailPrototypes[dp].dryColor; // renderMode clonedPrototype.renderMode = sourceTerrain.terrainData.detailPrototypes[dp].renderMode; workPrototypes[dp] = clonedPrototype; } workData.detailPrototypes = workPrototypes; // Tree Instances = TreeInstance[] TreeInstance[] workTrees = new TreeInstance[sourceTerrain.terrainData.treeInstances.Length]; for (int ti = 0; ti < workTrees.Length; ti++) { TreeInstance clonedTree = new TreeInstance(); // position clonedTree.position = sourceTerrain.terrainData.treeInstances[ti].position; // widthScale clonedTree.widthScale = sourceTerrain.terrainData.treeInstances[ti].widthScale; // heightScale clonedTree.heightScale = sourceTerrain.terrainData.treeInstances[ti].heightScale; // color clonedTree.color = sourceTerrain.terrainData.treeInstances[ti].color; // lightmapColor clonedTree.lightmapColor = sourceTerrain.terrainData.treeInstances[ti].lightmapColor; // prototypeIndex clonedTree.prototypeIndex = sourceTerrain.terrainData.treeInstances[ti].prototypeIndex; workTrees[ti] = clonedTree; } workData.treeInstances = workTrees; // Tree Prototypes = TreePrototype[] TreePrototype[] workTreePrototypes = new TreePrototype[sourceTerrain.terrainData.treePrototypes.Length]; for (int tp = 0; tp < workTreePrototypes.Length; tp++) { TreePrototype clonedTreePrototype = new TreePrototype(); // prefab clonedTreePrototype.prefab = sourceTerrain.terrainData.treePrototypes[tp].prefab; // bendFactor clonedTreePrototype.bendFactor = sourceTerrain.terrainData.treePrototypes[tp].bendFactor; workTreePrototypes[tp] = clonedTreePrototype; } workData.treePrototypes = workTreePrototypes; // Alphamap Layers TODO: READ ONLY :( // workData.alphamapLayers = sourceTerrain.terrainData.alphamapLayers; // Alphamap Resolution workData.alphamapResolution = sourceTerrain.terrainData.alphamapResolution; // Alphamap Width TODO: READ ONLY :( // workData.alphamapWidth = sourceTerrain.terrainData.alphamapWidth; // Alphamap Height TODO: READ ONLY :( // workData.alphamapHeight = sourceTerrain.terrainData.alphamapHeight; // Base Map Resolution workData.baseMapResolution = sourceTerrain.terrainData.baseMapResolution; // Splat Prototypes = SplatPrototype[] SplatPrototype[] workSplatPrototypes = new SplatPrototype[sourceTerrain.terrainData.splatPrototypes.Length]; for (int sp = 0; sp < workSplatPrototypes.Length; sp++) { SplatPrototype clonedSplatPrototype = new SplatPrototype(); // texture clonedSplatPrototype.texture = sourceTerrain.terrainData.splatPrototypes[sp].texture; // tileSize clonedSplatPrototype.tileSize = sourceTerrain.terrainData.splatPrototypes[sp].tileSize; // tileOffset clonedSplatPrototype.tileOffset = sourceTerrain.terrainData.splatPrototypes[sp].tileOffset; workSplatPrototypes[sp] = clonedSplatPrototype; } workData.splatPrototypes = workSplatPrototypes; // TODO: Figure out how to copy the resolutionPerPatch - currently hard coded to 16 workData.SetDetailResolution(sourceTerrain.terrainData.detailResolution, 16); float[,] sourceHeights = sourceTerrain.terrainData.GetHeights(0, 0, sourceTerrain.terrainData.heightmapResolution, sourceTerrain.terrainData.heightmapResolution); workData.SetHeights(0, 0, sourceHeights); float[,,] sourceAlphamaps = sourceTerrain.terrainData.GetAlphamaps(0, 0, sourceTerrain.terrainData.alphamapWidth, sourceTerrain.terrainData.alphamapHeight); workData.SetAlphamaps(0, 0, sourceAlphamaps); float[,,] newAlphamaps = workData.GetAlphamaps(0, 0, workData.alphamapWidth, workData.alphamapHeight); // Detail Layers int numDetailLayers = sourceTerrain.terrainData.detailPrototypes.Length; for (int layNum = 0; layNum < numDetailLayers; layNum++) { int[,] thisDetailLayer = sourceTerrain.terrainData.GetDetailLayer(0, 0, sourceTerrain.terrainData.detailWidth, sourceTerrain.terrainData.detailHeight, layNum); workData.SetDetailLayer(0, 0, layNum, thisDetailLayer); } // FUNCTIONS::: for (int dli = 0; dli < sourceTerrain.terrainData.detailPrototypes.Length; dli++) { int[,] curDetailLayer = sourceTerrain.terrainData.GetDetailLayer(0, 0, sourceTerrain.terrainData.detailResolution, sourceTerrain.terrainData.detailResolution, dli); workData.SetDetailLayer(0, 0, dli, curDetailLayer); } // Get Detail Layer // Set Detail Layer // Get Alphamaps // Set Alphamaps // Refresh Prototypes // Set Detail Resolution // Set Heights // Step 2: Terrain.CreateTerrainGameObject() // TODO: See if I really need this... Terrain workTerrain = Terrain.CreateTerrainGameObject(workData).GetComponent <Terrain>(); workTerrain.gameObject.name = desiredObjectName; // Step 3: Copy remaining settings // Terrain Data // DONE ABOVE // Tree Distance workTerrain.treeDistance = sourceTerrain.treeDistance; // Tree Billboard Distance workTerrain.treeBillboardDistance = sourceTerrain.treeBillboardDistance; // Tree Cros Fade Length workTerrain.treeCrossFadeLength = sourceTerrain.treeCrossFadeLength; // Tree Maximum Full LOD Count workTerrain.treeMaximumFullLODCount = sourceTerrain.treeMaximumFullLODCount; // Detail Object Distance workTerrain.detailObjectDistance = sourceTerrain.detailObjectDistance; // Detail Object Density workTerrain.detailObjectDensity = sourceTerrain.detailObjectDensity; // Heightmap Pixel Error workTerrain.heightmapPixelError = sourceTerrain.heightmapPixelError; // Heightmap Maximum LOD workTerrain.heightmapMaximumLOD = sourceTerrain.heightmapMaximumLOD; // Baseman Distance workTerrain.basemapDistance = sourceTerrain.basemapDistance; // Lightmap Index workTerrain.lightmapIndex = sourceTerrain.lightmapIndex; // Cast Shadows workTerrain.castShadows = sourceTerrain.castShadows; // Editor Render Flags // Step 4: Flush workData.RefreshPrototypes(); workTerrain.Flush(); // Step 5: Duplicate children for (int i = 0; i < sourceTerrain.transform.childCount; i++) { Transform child = sourceTerrain.transform.GetChild(i); GameObject newChild = Instantiate(child.gameObject) as GameObject; newChild.gameObject.name = child.gameObject.name; newChild.transform.parent = workTerrain.transform; } return(workTerrain.gameObject); }
public void SpawnTree(TreeType item) { if (item.collisionCheck) { RebuildCollisionCacheIfNeeded(); } item.instanceCount = 0; RefreshTreePrefabs(); float height, worldHeight, normalizedHeight; foreach (Terrain terrain in terrains) { List <TreeInstance> treeInstanceCollection = new List <TreeInstance>(terrain.terrainData.treeInstances); //Clear all existing instances first for (int i = 0; i < treeInstanceCollection.Count; i++) { foreach (TreePrefab prefab in item.prefabs) { treeInstanceCollection.RemoveAll(x => x.prototypeIndex == prefab.index); } } InitializeSeed(item.seed); item.spawnPoints = PoissonDisc.GetSpawnpoints(terrain, item.distance, item.seed + seed); foreach (Vector3 pos in item.spawnPoints) { //InitializeSeed(item.seed + index); //Relative position as 0-1 value Vector2 normalizedPos = terrain.GetNormalizedPosition(pos); if (item.collisionCheck) { //Check for collision if (InsideOccupiedCell(terrain, pos, normalizedPos)) { continue; } } InitializeSeed(item.seed + (int)pos.x + (int)pos.z); //Skip if failing global probability check if (((Random.value * 100f) <= item.probability) == false) { continue; } TreePrefab prefab = SpawnerBase.GetProbableTree(item); //Failed probabilty checks entirely if (prefab == null) { continue; } terrain.SampleHeight(normalizedPos, out height, out worldHeight, out normalizedHeight); if (item.rejectUnderwater && worldHeight < waterHeight) { continue; } //Check height if (worldHeight < item.heightRange.x || worldHeight > item.heightRange.y) { continue; } if (item.slopeRange.x > 0 || item.slopeRange.y < 90f) { float slope = terrain.GetSlope(normalizedPos, false); //Reject if slope check fails if (!(slope >= (item.slopeRange.x + 0.001f) && slope <= (item.slopeRange.y))) { continue; } } if (item.curvatureRange.x > 0 || item.curvatureRange.y < 1f) { float curvature = terrain.SampleConvexity(normalizedPos); //0=concave, 0.5=flat, 1=convex curvature = TerrainSampler.ConvexityToCurvature(curvature); if (curvature < item.curvatureRange.x || curvature > item.curvatureRange.y) { continue; } } //Reject based on layer masks Vector2Int texelIndex = terrain.SplatmapTexelIndex(normalizedPos); float spawnChance = 0f; if (item.layerMasks.Count == 0) { spawnChance = 100f; } foreach (TerrainLayerMask layer in item.layerMasks) { Texture2D splat = terrain.terrainData.GetAlphamapTexture(GetSplatmapID(layer.layerID)); Color color = splat.GetPixel(texelIndex.x, texelIndex.y); int channel = layer.layerID % 4; float value = SampleChannel(color, channel); if (value > 0) { value = Mathf.Clamp01(value - layer.threshold); } value *= 100f; spawnChance += value; } InitializeSeed((int)pos.x * (int)pos.z); if ((Random.value <= spawnChance) == false) { continue; } //Passed all conditions, add instance TreeInstance treeInstance = new TreeInstance(); treeInstance.prototypeIndex = prefab.index; treeInstance.position = new Vector3(normalizedPos.x, normalizedHeight, normalizedPos.y); treeInstance.rotation = Random.Range(0f, 359f) * Mathf.Deg2Rad; float scale = Random.Range(item.scaleRange.x, item.scaleRange.y); treeInstance.heightScale = scale; treeInstance.widthScale = scale; treeInstance.color = Color.white; treeInstanceCollection.Add(treeInstance); item.instanceCount++; } item.spawnPoints.Clear(); #if UNITY_2019_1_OR_NEWER terrain.terrainData.SetTreeInstances(treeInstanceCollection.ToArray(), false); #else terrain.terrainData.treeInstances = treeInstanceCollection.ToArray(); #endif } }
void AddTree() { TreePrototype[] trees = new TreePrototype[treeDataList.Count]; for (int i = 0; i < treeDataList.Count; i++) { trees[i] = new TreePrototype(); trees[i].prefab = treeDataList[i].treeMesh; } terrainData.treePrototypes = trees; List <TreeInstance> treeInstanceList = new List <TreeInstance>(); if (addTree) { for (int z = 0; z < terrainData.size.z; z += treeSpacing) { for (int x = 0; x < terrainData.size.x; x += treeSpacing) { for (int treePrototypeIndex = 0; treePrototypeIndex < trees.Length; treePrototypeIndex++) { if (treeInstanceList.Count < maxTrees) { float currentHeight = terrainData.GetHeight(x, z) / terrainData.size.y; if (currentHeight >= treeDataList[treePrototypeIndex].minHeight && currentHeight <= treeDataList[treePrototypeIndex].maxHeight) { float randomX = (x + Random.Range(-randomXRange, randomXRange)) / terrainData.size.x; float randomZ = (z + Random.Range(-randomZRange, randomZRange)) / terrainData.size.z; TreeInstance treeInstance = new TreeInstance(); treeInstance.position = new Vector3(randomX, currentHeight, randomZ); Vector3 treePosition = new Vector3(treeInstance.position.x * terrainData.size.x, treeInstance.position.y * terrainData.size.y, treeInstance.position.z * terrainData.size.z) + this.transform.position; RaycastHit raycastHit; int layerMask = 1 << terrainLayerIndex; if (Physics.Raycast(treePosition, Vector3.down, out raycastHit, 100, layerMask) || Physics.Raycast(treePosition, Vector3.up, out raycastHit, 100, layerMask)) { float treeHeight = (raycastHit.point.y - this.transform.position.y) / terrainData.size.y; treeInstance.position = new Vector3(treeInstance.position.x, treeHeight, treeInstance.position.z); treeInstance.rotation = Random.Range(0, 360); treeInstance.prototypeIndex = treePrototypeIndex; treeInstance.color = Color.white; treeInstance.lightmapColor = Color.white; treeInstance.heightScale = 0.95f; treeInstance.widthScale = 0.95f; treeInstanceList.Add(treeInstance); } } } } } } } terrainData.treeInstances = treeInstanceList.ToArray(); }
private IEnumerator RefreshRegion() { bProcessing = true; #region PREPARE_DATA yield return(0); m_allTreesInLayer.Clear(); m_mapPrototype.Clear(); m_listPrototype.Clear(); List <int> cnk_list_to_render = RSubTerrainMgr.Instance.ChunkListToRender(); int cnk_count = cnk_list_to_render.Count; for (int i = 0; i < cnk_count; ++i) { int idx = cnk_list_to_render[i]; List <TreeInfo> trees_in_zone = RSubTerrainMgr.ReadChunk(idx).TreeList; foreach (TreeInfo ti in trees_in_zone) { // Add tree float height = RSubTerrainMgr.Instance.GlobalPrototypeBounds[ti.m_protoTypeIdx].extents.y * 2F; // Trees in this layer if (RSubTerrainMgr.Instance.Layers[LayerIndex].MinTreeHeight <= height && height < RSubTerrainMgr.Instance.Layers[LayerIndex].MaxTreeHeight) { m_allTreesInLayer.Add(ti); // New prototype ? if (!m_mapPrototype.ContainsKey(ti.m_protoTypeIdx)) { int next_index = m_listPrototype.Count; m_mapPrototype.Add(ti.m_protoTypeIdx, next_index); m_listPrototype.Add(ti.m_protoTypeIdx); } } } } TreePrototype [] FinalPrototypeArray = new TreePrototype [m_listPrototype.Count]; for (int i = 0; i < m_listPrototype.Count; ++i) { FinalPrototypeArray[i] = new TreePrototype(); FinalPrototypeArray[i].bendFactor = RSubTerrainMgr.Instance.GlobalPrototypeBendFactorList[m_listPrototype[i]]; FinalPrototypeArray[i].prefab = RSubTerrainMgr.Instance.GlobalPrototypePrefabList[m_listPrototype[i]]; } yield return(0); int tree_cnt = m_allTreesInLayer.Count; TreeInstance [] FinalTreeInstanceArray = new TreeInstance [tree_cnt]; for (int t = 0; t < tree_cnt; ++t) { TreeInfo ti = m_allTreesInLayer[t]; Vector3 new_pos = ti.m_pos - _TargetPos; new_pos.x /= RSubTerrConstant.TerrainSize.x; new_pos.y /= RSubTerrConstant.TerrainSize.y; new_pos.z /= RSubTerrConstant.TerrainSize.z; #if false if (new_pos.x < 0 || new_pos.y < 0 || new_pos.z < 0 || new_pos.x > 1 || new_pos.y > 1 || new_pos.z > 1) { Debug.LogError("a tree was out of terrain bound, error!"); continue; } #endif if (m_mapPrototype.ContainsKey(ti.m_protoTypeIdx)) // Add key present check { FinalTreeInstanceArray[t].color = ti.m_clr; FinalTreeInstanceArray[t].heightScale = ti.m_heightScale; FinalTreeInstanceArray[t].widthScale = ti.m_widthScale; FinalTreeInstanceArray[t].lightmapColor = ti.m_lightMapClr; FinalTreeInstanceArray[t].position = new_pos; FinalTreeInstanceArray[t].prototypeIndex = m_mapPrototype[ti.m_protoTypeIdx]; } } yield return(0); #endregion #region ASSIGN_DATA gameObject.SetActive(false); m_TerrData.treeInstances = new TreeInstance[0]; m_TerrData.treePrototypes = FinalPrototypeArray; m_TerrData.treeInstances = FinalTreeInstanceArray; if (Application.isEditor) { _TreePrototypeCount = m_TerrData.treePrototypes.Length; _TreeInstanceCount = m_TerrData.treeInstances.Length; } transform.position = _TargetPos; gameObject.SetActive(true); #endregion bProcessing = false; }
private void DoRotate(float angle, Terrain newTerrain) { if (terrainTmp == null || origHeightMap == null) { grabOriginal = false; Debug.LogWarning("No terrain to rotate"); return; } isRotating = true; //Terrain terrain = o.GetComponent<Terrain>(); int nx, ny; float cs, sn; // heightmap rotation int tw = terrainTmp.terrainData.heightmapWidth; int th = terrainTmp.terrainData.heightmapHeight; float[,] newHeightMap = new float[tw, th]; float angleRad = angle * Mathf.Deg2Rad; float heightMiddle = (terrainTmp.terrainData.heightmapResolution) / 2.0f; // pivot at middle for (int y = 0; y < th; y++) { for (int x = 0; x < tw; x++) { cs = Mathf.Cos(angleRad); sn = Mathf.Sin(angleRad); nx = (int)((x - heightMiddle) * cs - (y - heightMiddle) * sn + heightMiddle); ny = (int)((x - heightMiddle) * sn + (y - heightMiddle) * cs + heightMiddle); if (nx < 0) { nx = 0; } if (nx > tw - 1) { nx = tw - 1; } if (ny < 0) { ny = 0; } if (ny > th - 1) { ny = th - 1; } newHeightMap[x, y] = origHeightMap[nx, ny]; } // for x } // for y // detail layer (grass, meshes) int dw = terrainTmp.terrainData.detailWidth; int dh = terrainTmp.terrainData.detailHeight; float detailMiddle = (terrainTmp.terrainData.detailResolution) / 2.0f; // pivot at middle int numDetails = terrainTmp.terrainData.detailPrototypes.Length; int[][,] newDetailLayer = new int[numDetails][, ]; // build new layer arrays for (int n = 0; n < numDetails; n++) { newDetailLayer[n] = new int[dw, dh]; } for (int z = 0; z < numDetails; z++) { for (int y = 0; y < dh; y++) { for (int x = 0; x < dw; x++) { cs = Mathf.Cos(angleRad); sn = Mathf.Sin(angleRad); nx = (int)((x - detailMiddle) * cs - (y - detailMiddle) * sn + detailMiddle); ny = (int)((x - detailMiddle) * sn + (y - detailMiddle) * cs + detailMiddle); if (nx < 0) { nx = 0; } if (nx > dw - 1) { nx = dw - 1; } if (ny < 0) { ny = 0; } if (ny > dh - 1) { ny = dh - 1; } newDetailLayer[z][x, y] = origDetailLayer[z][nx, ny]; } // for x } // for y } // for z // alpha layer (texture splatmap) rotation dw = terrainTmp.terrainData.alphamapWidth; dh = terrainTmp.terrainData.alphamapHeight; int dz = terrainTmp.terrainData.alphamapLayers; float alphaMiddle = (terrainTmp.terrainData.alphamapResolution) / 2.0f; // pivot at middle float[,,] newAlphaMap = new float[dw, dh, dz]; float[,,] origAlphaMapCopy; origAlphaMapCopy = origAlphaMap.Clone() as float[, , ]; for (int z = 0; z < dz; z++) { for (int y = 0; y < dh; y++) { for (int x = 0; x < dw; x++) { cs = Mathf.Cos(angleRad); sn = Mathf.Sin(angleRad); nx = (int)((x - alphaMiddle) * cs - (y - alphaMiddle) * sn + alphaMiddle); ny = (int)((x - alphaMiddle) * sn + (y - alphaMiddle) * cs + alphaMiddle); if (nx < 0) { nx = 0; } if (nx > dw - 1) { nx = dw - 1; } if (ny < 0) { ny = 0; } if (ny > dh - 1) { ny = dh - 1; } newAlphaMap[x, y, z] = origAlphaMapCopy[nx, ny, z]; } // for x } // for y } // for z // trees rotation, one by one.. // TODO: use list instead, then can remove trees outside the terrain int treeCount = terrainTmp.terrainData.treeInstances.Length; TreeInstance[] newTrees = new TreeInstance[treeCount]; Vector3 newTreePos = Vector3.zero; float tx, tz; for (int n = 0; n < treeCount; n++) { cs = Mathf.Cos(angleRad); sn = Mathf.Sin(angleRad); tx = origTrees[n].position.x - 0.5f; tz = origTrees[n].position.z - 0.5f; newTrees[n] = origTrees[n]; newTreePos.x = (cs * tx) - (sn * tz) + 0.5f; newTreePos.y = origTrees[n].position.y; newTreePos.z = (cs * tz) + (sn * tx) + 0.5f; newTrees[n].position = newTreePos; } // for treeCount // this is too slow in unity.. //Undo.RecordObject(terrain.terrainData,"Rotate terrain ("+angle+")"); // Apply new data to terrain //newTerrain.terrainData.treeInstances = newTrees; newTerrain.terrainData.treeInstances = new TreeInstance[] { /*newTrees[0]*/ }; //Just a test to delete tree colliders newTerrain.terrainData.SetHeightsDelayLOD(0, 0, newHeightMap); // splitting up SetHeights part1 newTerrain.ApplyDelayedHeightmapModification(); //part2 //newTerrain.terrainData.treeInstances = newTrees; newTerrain.terrainData.SetAlphamaps(0, 0, newAlphaMap); for (int n = 0; n < terrainTmp.terrainData.detailPrototypes.Length; n++) { newTerrain.terrainData.SetDetailLayer(0, 0, n, newDetailLayer[n]); } // we are done.. isRotating = false; } //TerrainRotate
public void TerrainToolboxUtilites_WhenSplitTerrain_MissingTrees(int amountOfTreesX, int amountOfTreesZ, int tileXAxis, int tileZAxis) { //Setup tree prefab (Needs to be persistent) GameObject treePrefab = GameObject.CreatePrimitive(PrimitiveType.Cube); treePrefab.GetComponent <Renderer>().sharedMaterial.shader = Shader.Find("Nature/Tree Soft Occlusion Bark"); string localPath = $"Assets/{treePrefab.name}.prefab"; localPath = AssetDatabase.GenerateUniqueAssetPath(localPath); PrefabUtility.SaveAsPrefabAsset(treePrefab, localPath); treePrefab = AssetDatabase.LoadAssetAtPath(localPath, typeof(GameObject)) as GameObject; //Setup terrain object with trees TerrainData terrainData = m_TerrainComponent.terrainData; TreePrototype prototype = new TreePrototype(); prototype.prefab = treePrefab; terrainData.treePrototypes = new TreePrototype[] { prototype }; TreeInstance[] treeInstancesArray = new TreeInstance[amountOfTreesX * amountOfTreesZ]; for (int z = 0; z < amountOfTreesZ; z++) { for (int x = 0; x < amountOfTreesX; x++) { TreeInstance treeInstance = new TreeInstance(); treeInstance.prototypeIndex = 0; treeInstance.position = new Vector3(x / (float)amountOfTreesX, 0, z / (float)amountOfTreesZ); treeInstancesArray[(z * amountOfTreesZ) + x] = treeInstance; } } terrainData.treeInstances = treeInstancesArray; // Set up parent object so we can locate the split tiles for cleanup after testing int groupingId = 12345; var parent = new GameObject().AddComponent <TerrainGroup>(); parent.GroupID = groupingId; m_TerrainComponent.transform.SetParent(parent.transform); //Execute the repro steps checking to make sure split terrains have trees Selection.activeGameObject = m_TerrainGO; TerrainToolboxWindow toolboxWindow = EditorWindow.GetWindow(typeof(TerrainToolboxWindow)) as TerrainToolboxWindow; toolboxWindow.m_TerrainUtilitiesMode.m_Settings.KeepOldTerrains = true; toolboxWindow.m_TerrainUtilitiesMode.m_Settings.TileXAxis = tileXAxis; toolboxWindow.m_TerrainUtilitiesMode.m_Settings.TileZAxis = tileZAxis; toolboxWindow.m_TerrainUtilitiesMode.SplitTerrains(true); Terrain[] objs = GameObject.FindObjectsOfType <Terrain>(); Terrain[] splitTerrains = objs.Where( obj => obj.terrainData?.treeInstanceCount > 0 ).ToArray(); Assert.IsNotEmpty(splitTerrains); //Cleanup toolboxWindow.Close(); FileUtil.DeleteFileOrDirectory("Assets/Terrain"); File.Delete("Assets/Terrain.meta"); File.Delete(localPath); File.Delete(localPath + ".meta"); UnityEditor.AssetDatabase.Refresh(); }
void Setup() { if (target.terrainTemplate == null) { throw new UnityException("No terrain template assigned."); } if (target.terrainTemplate.terrainData == null) { throw new UnityException("Terrain template has no terrain data."); } if (target.terrainTemplate.materialTemplate == null) { throw new UnityException("Terrain template has no base material template data."); } var ooBoundsSize = target.transform.localScale; var ooBoundsExtents = ooBoundsSize * 0.5f; var targetPos = target.transform.position; var targetRight = target.transform.right; var targetForward = target.transform.forward; var ooPlanes = new [] { new Plane(targetRight, targetPos + targetRight * ooBoundsExtents.x), new Plane(-targetRight, targetPos - targetRight * ooBoundsExtents.x), new Plane(targetForward, targetPos + targetForward * ooBoundsExtents.z), new Plane(-targetForward, targetPos - targetForward * ooBoundsExtents.z), }; var aaBounds = new Bounds(Vector3.zero, Vector3.one); aaBounds.Encapsulate(targetRight * ooBoundsExtents.x + Vector3.up * ooBoundsExtents.y + targetForward * ooBoundsExtents.z); aaBounds.Encapsulate(-targetRight * ooBoundsExtents.x + -Vector3.up * ooBoundsExtents.y + -targetForward * ooBoundsExtents.z); aaBounds.Encapsulate(-targetRight * ooBoundsExtents.x + Vector3.up * ooBoundsExtents.y + targetForward * ooBoundsExtents.z); aaBounds.Encapsulate(targetRight * ooBoundsExtents.x + -Vector3.up * ooBoundsExtents.y + -targetForward * ooBoundsExtents.z); var aaBoundsSize = aaBounds.size; var aaBoundsExtents = aaBounds.extents; if (target.autoColliders) { // GeometryUtility.TestPlanesAABB won't accept my OO planes, so // let's just use a world space AABB test instead. var aaBoundsWorld = new Bounds(targetPos + aaBounds.center, aaBounds.size); foreach (var mr in Object.FindObjectsOfType <MeshRenderer>()) { if (mr.GetComponent <Collider>() == null) { if (aaBoundsWorld.Intersects(mr.bounds)) { target.autoColliderList.Add(mr.gameObject.AddComponent <MeshCollider>()); } } } } var terrainCorner = targetPos - aaBoundsExtents; var xStepCount = Mathf.NextPowerOfTwo(Mathf.CeilToInt(aaBoundsSize.x * target.density)); var zStepCount = Mathf.NextPowerOfTwo(Mathf.CeilToInt(aaBoundsSize.z * target.density)); int ZM = zStepCount; int XM = xStepCount; int XZM = Mathf.Max(XM, ZM); var xStep = (float)XM / (float)XZM * aaBoundsSize.x / (float)xStepCount; var zStep = (float)ZM / (float)XZM * aaBoundsSize.z / (float)zStepCount; GameObject[] paintingObjects = target.paintingObjects; GameObject[] blockingObjects = target.blockingObjects; bool hasExplicitPaint = paintingObjects != null && paintingObjects.Length > 0; bool hasExplicitBlock = blockingObjects != null && blockingObjects.Length > 0; LayerMask paintingLayers = hasExplicitPaint ? (LayerMask) ~0 : target.paintingLayers; LayerMask blockingLayers = hasExplicitBlock ? (LayerMask) ~0 : target.blockingLayers; float rayLength = aaBoundsSize.y; float heightScale = 1f / rayLength; var castRadius = target.castRadius; var heightmap = new float[XZM, XZM]; var maskmap = new float[XZM, XZM]; var oldSelfTerrainCollider = target.workingObject ? target.workingObject.GetComponent <TerrainCollider>() : null; for (int z = 0; z < XZM; ++z) { for (int x = 0; x < XZM; ++x) { var pos = terrainCorner + new Vector3(x * xStep, aaBoundsSize.y, z * zStep); if (ooPlanes[0].GetSide(pos) || ooPlanes[1].GetSide(pos) || ooPlanes[2].GetSide(pos) || ooPlanes[3].GetSide(pos)) { heightmap[z, x] = 0f; maskmap[z, x] = 0f; continue; } float targetDist = rayLength; float targetMask = 1f; var rhisDown = Physics.SphereCastAll(pos, castRadius, Vector3.down, rayLength, paintingLayers); if (rhisDown.Length > 0) { for (int i = 0, n = rhisDown.Length; i < n; ++i) { var rhiDown = rhisDown[i]; var validTerrain = !(rhiDown.collider is TerrainCollider) || (target.allowTerrainColliders && (oldSelfTerrainCollider == null || rhiDown.collider != oldSelfTerrainCollider)); if ( validTerrain && (!hasExplicitPaint || System.Array.IndexOf(paintingObjects, rhiDown.collider.gameObject) != -1) && System.Array.IndexOf(blockingObjects, rhiDown.collider.gameObject) == -1 ) { var curDist = pos.y - rhiDown.point.y; // rhiDown.distance + castRadius; if (curDist < targetDist) { targetMask = MaskSlope(rhiDown.normal.y); targetDist = curDist; } } } } bool hasClearance = true; if (targetDist < rayLength && (hasExplicitBlock || blockingLayers.value != 0)) { float blockedDist = rayLength; var rhisDownBlockers = Physics.SphereCastAll(pos, castRadius, Vector3.down, rayLength, blockingLayers); for (int i = 0, n = rhisDownBlockers.Length; i < n; ++i) { var rhiDown = rhisDownBlockers[i]; if (!(rhiDown.collider is TerrainCollider)) { if (!hasExplicitBlock || System.Array.IndexOf(blockingObjects, rhiDown.collider.gameObject) != -1) { if (!hasExplicitPaint || System.Array.IndexOf(paintingObjects, rhiDown.collider.gameObject) == -1) { blockedDist = Mathf.Min(blockedDist, pos.y - rhiDown.point.y /*rhiDown.distance + castRadius*/); } } } } if (blockedDist < targetDist) { const float cBelow = 25f; var upPos = pos + Vector3.down * (targetDist + cBelow); RaycastHit[] rhisUpBlockers = Physics.SphereCastAll(upPos, castRadius, Vector3.up, targetDist + cBelow, blockingLayers); for (int i = 0, n = rhisUpBlockers.Length; i < n && hasClearance; ++i) { var rhiUp = rhisUpBlockers[i]; if (!(rhiUp.collider is TerrainCollider)) { if (!hasExplicitBlock || System.Array.IndexOf(blockingObjects, rhiUp.collider.gameObject) != -1) { if (!hasExplicitPaint || System.Array.IndexOf(paintingObjects, rhiUp.collider.gameObject) == -1) { hasClearance = rhiUp.point.y - upPos.y /*rhiUp.distance + castRadius*/ - cBelow > target.heightClearance; } } } } } } bool hasValidSample = hasClearance && targetDist != rayLength; heightmap[z, x] = hasValidSample ? Mathf.Clamp01(1f - targetDist * heightScale) : 0f; maskmap[z, x] = hasValidSample ? targetMask : 0f; } } target.heightData = new float[XZM * XZM]; System.Buffer.BlockCopy(heightmap, 0, target.heightData, 0, target.heightData.Length * sizeof(float)); target.maskData = new float[XZM * XZM]; System.Buffer.BlockCopy(maskmap, 0, target.maskData, 0, target.maskData.Length * sizeof(float)); var ttc = target.terrainTemplate; var ttd = ttc.terrainData; if (target.workingObject) { target.workingObject.transform.position += Vector3.up * target.pickingHackFixBias; } var oldTC = target.workingObject ? target.workingObject.GetComponent <Terrain>() : null; //if(oldTC && oldTC.GetComponent<TerrainCollider>()) // oldTC.gameObject.AddComponent<TerrainCollider>(); var td = oldTC ? oldTC.terrainData : new TerrainData(); td.SetDetailResolution(XZM, 32); td.heightmapResolution = XZM; td.SetHeights(0, 0, heightmap); td.size = aaBoundsSize; td.splatPrototypes = ttd.splatPrototypes; td.detailPrototypes = ttd.detailPrototypes; td.treePrototypes = ttd.treePrototypes; for (int i = 0, offset = 0, step = target.detailLayerElements * sizeof(int), n = td.detailPrototypes.Length, m = target.detailLayerCount; i < n && i < m; ++i, offset += step) { var details = td.GetDetailLayer(0, 0, td.detailWidth, td.detailHeight, i); System.Buffer.BlockCopy(target.detailData, offset, details, 0, step); td.SetDetailLayer(0, 0, i, details); } if (target.treeInstances != null) { var tdi = new TreeInstance[target.treeInstances.Length]; for (int i = 0, n = target.treeInstances.Length; i < n; ++i) { tdi[i] = (TreeInstance)target.treeInstances[i]; } td.treeInstances = tdi; } var terrain = oldTC ? oldTC.gameObject : Terrain.CreateTerrainGameObject(td); if (oldTC == null) { var helper = terrain.AddComponent <PaintJobProxy>(); while (UnityEditorInternal.ComponentUtility.MoveComponentUp(helper)) { ; } } var tc = terrain.GetComponent <Terrain>(); tc.detailObjectDistance = 1000f; //ttc.detailObjectDistance; tc.detailObjectDensity = ttc.detailObjectDensity; target.materialInstance = tc.materialTemplate = new Material(ttc.materialTemplate); tc.castShadows = false; terrain.transform.parent = target.transform; terrain.transform.position = terrainCorner; terrain.transform.localRotation = Quaternion.identity; terrain.transform.localScale = Vector3.one; target.workingObject = terrain; target.isPainting = true; target.alignedSize = aaBoundsSize; target.materialInstance.SetFloat("_HackCullScale", 1f); tc.drawHeightmap = true; tc.terrainData.wavingGrassAmount = 0f; tc.terrainData.wavingGrassSpeed = 0f; tc.terrainData.wavingGrassStrength = 0f; tc.terrainData.wavingGrassTint = Color.white; }
/// <summary> /// Return a rotation of a tree instance /// </summary> /// <param name="treeInstance">the tree instance</param> /// <returns>A world space rotation from the tree instance</returns> public static Quaternion GetWorldRotation(this TreeInstance treeInstance) { return(Quaternion.AngleAxis(treeInstance.rotation * Mathf.Rad2Deg, Vector3.up)); }
/// <summary> /// Return a scale of a tree instance /// </summary> /// <param name="treeInstance">the tree instance</param> /// <returns>A world space scale from the tree instance</returns> public static Vector3 GetWorldScale(this TreeInstance treeInstance) { return(new Vector3(treeInstance.widthScale, treeInstance.heightScale, treeInstance.widthScale)); }
static void RegenerateForest() { Forest forest = GameObject.FindObjectOfType(typeof(Forest)) as Forest; Selection.activeGameObject = forest.gameObject; int layerMask = ~0; int progressUpdateRate = forest.m_instanceCount / 200; foreach (var mask in forest.m_ignoreLayers) { layerMask &= ~(1 << mask); Debug.Log("Layer Mask: " + layerMask); } while (forest.transform.childCount > 0) { GameObject.DestroyImmediate(forest.transform.GetChild(0).gameObject); } forest.RebuildSections(); bool cancel = false; int treesPlanted = 0; // TODO: This won't ever reach the target tree count as it gives up following a collision. for (int treeCount = 0; treeCount < forest.m_instanceCount && !cancel; ++treeCount) { bool succeeded = false; int counter = 0; while (!succeeded && counter < forest.m_retryBailout) { float x = Random.Range(forest.m_startX, forest.m_endX); float z = Random.Range(forest.m_startZ, forest.m_endZ); Vector3 position = new Vector3(x, 0.0f, z); bool overlap = Physics.CheckCapsule((Vector3)position + new Vector3(0.0f, -50.0f, 0.0f), (Vector3)position + new Vector3(0.0f, 50.0f, 0.0f), forest.m_treeRadius, layerMask); bool terrainValid = true; RaycastHit hitInfo; if (Physics.Raycast(new Vector3(x, 1.0f, z), new Vector3(0.0f, -1.0f, 0.0f), out hitInfo, 50.0f, ~LayerMask.NameToLayer("WorldCollision"))) { if (hitInfo.point.y < -0.01f) { terrainValid = false; } } if (!overlap && terrainValid) { try { TreeInstance instance = new TreeInstance(); instance.position = position; instance.radius = UnityEngine.Random.Range(1.0f, 3.5f); succeeded = !forest.AddInstance(instance); if (succeeded) { treesPlanted++; } } catch (System.Exception e) { Debug.Log("Instance out of bound at " + position.x + ", " + position.z); Debug.Log(e); Debug.Break(); } counter++; if (treeCount % progressUpdateRate == 0) { cancel = EditorUtility.DisplayCancelableProgressBar("Generating Forest", "Creating trees (" + treeCount + ", " + forest.m_instanceCount + ")", (float)treeCount / (float)forest.m_instanceCount); } } } } EditorUtility.ClearProgressBar(); Debug.Log("Planted " + treesPlanted + " trees"); }
public void SerializeTerrainData(List <byte> bytes, TerrainData terrainData) { R_SerializationHelper.SerializeString(bytes, terrainData.name); int heightmapResolution = terrainData.heightmapResolution; int detailResolution = terrainData.detailResolution; R_SerializationHelper.SerializeInt(bytes, heightmapResolution); R_SerializationHelper.SerializeInt(bytes, terrainData.baseMapResolution); R_SerializationHelper.SerializeInt(bytes, terrainData.alphamapResolution); R_SerializationHelper.SerializeInt(bytes, detailResolution); R_SerializationHelper.SerializeVector3(bytes, terrainData.size); R_SerializationHelper.SerializeFloat(bytes, terrainData.thickness); R_SerializationHelper.SerializeFloat(bytes, terrainData.wavingGrassAmount); R_SerializationHelper.SerializeFloat(bytes, terrainData.wavingGrassSpeed); R_SerializationHelper.SerializeFloat(bytes, terrainData.wavingGrassStrength); R_SerializationHelper.SerializeColor(bytes, terrainData.wavingGrassTint); // Splat textures SplatPrototype[] splatPrototypes = terrainData.splatPrototypes; R_SerializationHelper.SerializeInt(bytes, splatPrototypes.Length); for (int i = 0; i < splatPrototypes.Length; i++) { SplatPrototype splat = splatPrototypes[i]; R_SerializationHelper.SerializeFloat(bytes, splat.metallic); if (splat.normalMap != null) { bytes.Add(1); R_SerializationHelper.SerializeString(bytes, splat.normalMap.name); } else { bytes.Add(0); } R_SerializationHelper.SerializeFloat(bytes, splat.smoothness); R_SerializationHelper.SerializeString(bytes, splat.texture.name); R_SerializationHelper.SerializeVector2(bytes, splat.tileOffset); R_SerializationHelper.SerializeVector2(bytes, splat.tileSize); } // Tree Prototypes TreePrototype[] treePrototypes = terrainData.treePrototypes; R_SerializationHelper.SerializeInt(bytes, treePrototypes.Length); for (int i = 0; i < treePrototypes.Length; i++) { TreePrototype tree = treePrototypes[i]; R_SerializationHelper.SerializeFloat(bytes, tree.bendFactor); R_SerializationHelper.SerializeString(bytes, tree.prefab.name); } // Grass DetailPrototype[] detailPrototypes = terrainData.detailPrototypes; int detailPrototypesLength = detailPrototypes.Length; R_SerializationHelper.SerializeInt(bytes, detailPrototypes.Length); for (int i = 0; i < detailPrototypes.Length; i++) { DetailPrototype detail = detailPrototypes[i]; R_SerializationHelper.SerializeFloat(bytes, detail.bendFactor); R_SerializationHelper.SerializeColor(bytes, detail.dryColor); R_SerializationHelper.SerializeColor(bytes, detail.healthyColor); R_SerializationHelper.SerializeFloat(bytes, detail.maxHeight); R_SerializationHelper.SerializeFloat(bytes, detail.maxWidth); R_SerializationHelper.SerializeFloat(bytes, detail.minHeight); R_SerializationHelper.SerializeFloat(bytes, detail.minWidth); R_SerializationHelper.SerializeFloat(bytes, detail.noiseSpread); if (detail.prototype != null) { bytes.Add(1); R_SerializationHelper.SerializeString(bytes, detail.prototype.name); } else { bytes.Add(0); } if (detail.prototypeTexture != null) { bytes.Add(1); R_SerializationHelper.SerializeString(bytes, detail.prototypeTexture.name); } else { bytes.Add(0); } R_SerializationHelper.SerializeInt(bytes, (int)detail.renderMode); } // Heights float[,] heights = terrainData.GetHeights(0, 0, heightmapResolution, heightmapResolution); R_SerializationHelper.Serialize2DFloatArray(bytes, heights); // Splat maps R_SerializationHelper.SerializeInt(bytes, terrainData.alphamapTextures.Length); for (int i = 0; i < terrainData.alphamapTextures.Length; i++) { Texture2D tex = terrainData.alphamapTextures[i]; byte[] texBytes = tex.EncodeToPNG(); R_SerializationHelper.SerializeInt(bytes, texBytes.Length); bytes.AddRange(texBytes); } // Trees TreeInstance[] treeInstances = terrainData.treeInstances; R_SerializationHelper.SerializeInt(bytes, treeInstances.Length); for (int i = 0; i < treeInstances.Length; i++) { TreeInstance tree = treeInstances[i]; R_SerializationHelper.SerializeColor(bytes, tree.color); R_SerializationHelper.SerializeFloat(bytes, tree.heightScale); R_SerializationHelper.SerializeColor(bytes, tree.lightmapColor); R_SerializationHelper.SerializeVector3(bytes, tree.position); R_SerializationHelper.SerializeInt(bytes, tree.prototypeIndex); R_SerializationHelper.SerializeFloat(bytes, tree.rotation); R_SerializationHelper.SerializeFloat(bytes, tree.widthScale); } // Grass for (int i = 0; i < detailPrototypesLength; i++) { int[,] detailMap = terrainData.GetDetailLayer(0, 0, detailResolution, detailResolution, i); R_SerializationHelper.Serialize2DIntArrayToBytes(bytes, detailMap); } }
public TerrainData DeserializeTerrainData(byte[] bytes, ref int index) { TerrainData terrainData = new TerrainData(); terrainData.name = R_SerializationHelper.DeserializeString(bytes, ref index); // Debug.Log(terrainData.name); int heightmapResolution = R_SerializationHelper.DeserializeInt(bytes, ref index); terrainData.heightmapResolution = heightmapResolution; terrainData.baseMapResolution = R_SerializationHelper.DeserializeInt(bytes, ref index); terrainData.alphamapResolution = R_SerializationHelper.DeserializeInt(bytes, ref index); int detailResolution = R_SerializationHelper.DeserializeInt(bytes, ref index); terrainData.SetDetailResolution(detailResolution, 16); terrainData.size = R_SerializationHelper.DeserializeVector3(bytes, ref index); terrainData.thickness = R_SerializationHelper.DeserializeFloat(bytes, ref index); terrainData.wavingGrassAmount = R_SerializationHelper.DeserializeFloat(bytes, ref index); terrainData.wavingGrassSpeed = R_SerializationHelper.DeserializeFloat(bytes, ref index); terrainData.wavingGrassStrength = R_SerializationHelper.DeserializeFloat(bytes, ref index); terrainData.wavingGrassTint = R_SerializationHelper.DeserializeColor(bytes, ref index); // Splat Textures int splatLength = R_SerializationHelper.DeserializeInt(bytes, ref index); SplatPrototype[] splatPrototypes = new SplatPrototype[splatLength]; for (int i = 0; i < splatLength; i++) { SplatPrototype splat = new SplatPrototype(); splat.metallic = R_SerializationHelper.DeserializeFloat(bytes, ref index); if (bytes[index++] == 1) { string normalName = R_SerializationHelper.DeserializeString(bytes, ref index); splat.normalMap = (Texture2D)Resources.Load(normalName); } splat.smoothness = R_SerializationHelper.DeserializeFloat(bytes, ref index); string splatName = R_SerializationHelper.DeserializeString(bytes, ref index); splat.texture = (Texture2D)Resources.Load(splatName); splat.tileOffset = R_SerializationHelper.DeserializeVector2(bytes, ref index); splat.tileSize = R_SerializationHelper.DeserializeVector2(bytes, ref index); splatPrototypes[i] = splat; } terrainData.splatPrototypes = splatPrototypes; // Tree Prototypes int treeLength = R_SerializationHelper.DeserializeInt(bytes, ref index); TreePrototype[] treePrototypes = new TreePrototype[treeLength]; for (int i = 0; i < treeLength; i++) { TreePrototype tree = new TreePrototype(); tree.bendFactor = R_SerializationHelper.DeserializeFloat(bytes, ref index); string prefabName = R_SerializationHelper.DeserializeString(bytes, ref index); tree.prefab = (GameObject)Resources.Load(prefabName); treePrototypes[i] = tree; } terrainData.treePrototypes = treePrototypes; // Grass Prototypes int grassLength = R_SerializationHelper.DeserializeInt(bytes, ref index); DetailPrototype[] detailPrototypes = new DetailPrototype[grassLength]; for (int i = 0; i < grassLength; i++) { DetailPrototype grass = new DetailPrototype(); grass.bendFactor = R_SerializationHelper.DeserializeFloat(bytes, ref index); grass.dryColor = R_SerializationHelper.DeserializeColor(bytes, ref index); grass.healthyColor = R_SerializationHelper.DeserializeColor(bytes, ref index); grass.maxHeight = R_SerializationHelper.DeserializeFloat(bytes, ref index); grass.maxWidth = R_SerializationHelper.DeserializeFloat(bytes, ref index); grass.minHeight = R_SerializationHelper.DeserializeFloat(bytes, ref index); grass.minWidth = R_SerializationHelper.DeserializeFloat(bytes, ref index); grass.noiseSpread = R_SerializationHelper.DeserializeFloat(bytes, ref index); if (bytes[index++] == 1) { R_SerializationHelper.DeserializeString(bytes, ref index); } if (bytes[index++] == 1) { string textureName = R_SerializationHelper.DeserializeString(bytes, ref index); grass.prototypeTexture = (Texture2D)Resources.Load(textureName); } grass.renderMode = (DetailRenderMode)R_SerializationHelper.DeserializeInt(bytes, ref index); detailPrototypes[i] = grass; } terrainData.detailPrototypes = detailPrototypes; float[,] heights = R_SerializationHelper.Deserialize2DFloatArray(bytes, ref index); terrainData.SetHeights(0, 0, heights); int splatmapLength = R_SerializationHelper.DeserializeInt(bytes, ref index); Texture2D[] alphamapTextures = terrainData.alphamapTextures; for (int i = 0; i < splatmapLength; i++) { int length = R_SerializationHelper.DeserializeInt(bytes, ref index); byte[] texBytes = new byte[length]; Array.Copy(bytes, index, texBytes, 0, length); index += length; alphamapTextures[i].LoadImage(texBytes); alphamapTextures[i].Apply(); } int treeInstancesLength = R_SerializationHelper.DeserializeInt(bytes, ref index); TreeInstance[] trees = new TreeInstance[treeInstancesLength]; for (int i = 0; i < trees.Length; i++) { TreeInstance tree = new TreeInstance(); tree.color = R_SerializationHelper.DeserializeColor(bytes, ref index); tree.heightScale = R_SerializationHelper.DeserializeFloat(bytes, ref index); tree.lightmapColor = R_SerializationHelper.DeserializeColor(bytes, ref index); tree.position = R_SerializationHelper.DeserializeVector3(bytes, ref index); tree.prototypeIndex = R_SerializationHelper.DeserializeInt(bytes, ref index); tree.rotation = R_SerializationHelper.DeserializeFloat(bytes, ref index); tree.widthScale = R_SerializationHelper.DeserializeFloat(bytes, ref index); trees[i] = tree; } terrainData.treeInstances = trees; for (int i = 0; i < grassLength; i++) { int[,] grassMap = R_SerializationHelper.Deserialize2DByteArrayToInt(bytes, ref index); terrainData.SetDetailLayer(0, 0, i, grassMap); } return(terrainData); }
public void PlantVegetation() { TreePrototype[] newTreePrototypes; newTreePrototypes = new TreePrototype[vegetation.Count]; int tindex = 0; foreach (Vegetation t in vegetation) { newTreePrototypes[tindex] = new TreePrototype(); newTreePrototypes[tindex].prefab = t.treeMesh; tindex++; } terrainData.treePrototypes = newTreePrototypes; List <TreeInstance> allVegetation = new List <TreeInstance>(); for (int z = 0; z < terrainData.size.z; z += (vegTreeSpacing)) { for (int x = 0; x < terrainData.size.x; x += vegTreeSpacing) { for (int tp = 0; tp < terrainData.treePrototypes.Length; tp++) { if (Random.Range(0.0f, 1.0f) > vegetation[tp].density) { break; } float thisHeight = terrainData.GetHeight(x, z) / terrainData.size.y; float thisHeightStart = vegetation[tp].minHeight; float thisHeightEnd = vegetation[tp].maxHeight; float steepness = terrainData.GetSteepness(x / (float)terrainData.size.x, z / (float)terrainData.size.z); if ((thisHeightEnd >= thisHeight && thisHeightStart <= thisHeight) && (steepness >= vegetation[tp].minSlope && steepness <= vegetation[tp].maxSlope)) { TreeInstance instance = new TreeInstance(); instance.position = new Vector3((x + Random.Range(-vegRandom, vegRandom)) / terrainData.size.x, thisHeight, (z + Random.Range(-vegRandom, vegRandom)) / terrainData.size.z); Vector3 treeWorldPos = new Vector3(instance.position.x * terrainData.size.x, instance.position.y * terrainData.size.y, instance.position.z * terrainData.size.z) + transform.position; RaycastHit hit; int layerMask = 1 << terrainLayer; if (Physics.Raycast(treeWorldPos + new Vector3(0, 10, 0), -Vector3.up, out hit, 100, layerMask) || Physics.Raycast(treeWorldPos - new Vector3(0, 10, 0), Vector3.up, out hit, 100, layerMask)) { float treeHeight = (hit.point.y - this.transform.position.y) / terrainData.size.y; instance.position = new Vector3(instance.position.x, treeHeight, instance.position.z); instance.rotation = Random.Range(vegetation[tp].minRotation, vegetation[tp].maxRotation); instance.prototypeIndex = tp; instance.color = Color.Lerp(vegetation[tp].colour1, vegetation[tp].colour2, Random.Range(0.0f, 1.0f)); instance.lightmapColor = vegetation[tp].lightColour; float scale = Random.Range(vegetation[tp].minScale, vegetation[tp].maxScale); instance.heightScale = scale + Random.Range(-0.6f, 0.6f); instance.widthScale = scale + Random.Range(-0.6f, 0.6f); allVegetation.Add(instance); if (allVegetation.Count >= vegTreeMax) { goto TREESDONE; } } } } } } TREESDONE: terrainData.treeInstances = allVegetation.ToArray(); }
/// <summary> /// Populates the target list with a list of map trees or props. /// </summary> protected override void TargetList() { // List of prefabs that have passed filtering. List <PropListItem> itemList = new List <PropListItem>(); // Local references. TreeManager treeManager = Singleton <TreeManager> .instance; TreeInstance[] trees = treeManager.m_trees.m_buffer; PropManager propManager = Singleton <PropManager> .instance; PropInstance[] props = propManager.m_props.m_buffer; // Iterate through each tree instance map. for (int index = 0; index < (IsTree ? trees.Length : props.Length); ++index) { // Create new list item, hiding probabilities. PropListItem propListItem = new PropListItem { showProbs = false }; if (IsTree) { // Local reference. TreeInstance tree = trees[index]; // Skip non-existent trees (those with no flags). if (tree.m_flags == (ushort)TreeInstance.Flags.None) { continue; } // Try to get any tree replacement. propListItem.originalPrefab = MapTreeReplacement.instance.GetOriginal(tree.Info); // DId we find a current replacment? if (propListItem.originalPrefab == null) { // No - set current item as the original tree. propListItem.originalPrefab = tree.Info; } else { // Yes - record current item as replacement. propListItem.replacementPrefab = tree.Info; } } else { // Props. PropInstance prop = props[index]; // Skip non-existent props (those with no flags). if (prop.m_flags == (ushort)PropInstance.Flags.None) { continue; } // Try to get any prop replacement. propListItem.originalPrefab = MapPropReplacement.instance.GetOriginal(prop.Info); // DId we find a current replacment? if (propListItem.originalPrefab == null) { // No - set current item as the original prop. propListItem.originalPrefab = prop.Info; } else { // Yes - record current item as replacement. propListItem.replacementPrefab = prop.Info; } } // Check to see if we were succesful - if not (e.g. we only want trees and this is a prop), continue on to next instance. if (propListItem.originalPrefab?.name == null) { continue; } // Map instances are always grouped, and we don't have lists of indexes - too many trees! propListItem.index = -1; // Are we grouping? if (propListItem.index == -1) { // Yes, grouping - initialise a flag to show if we've matched. bool matched = false; // Iterate through each item in our existing list of props. foreach (PropListItem item in itemList) { // Check to see if we already have this in the list - matching original prefab. if (item.originalPrefab == propListItem.originalPrefab) { // We've already got an identical grouped instance of this item - set the flag to indicate that we've match it. matched = true; // No point going any further through the list, since we've already found our match. break; } } // Did we get a match? if (matched) { // Yes - continue on to next tree (without adding this item separately to the list). continue; } } // Add this item to our list. itemList.Add(propListItem); } // Create return fastlist from our filtered list, ordering by name. targetList.m_rowsData = new FastList <object> { m_buffer = targetSearchStatus == (int)OrderBy.NameDescending ? itemList.OrderByDescending(item => item.DisplayName).ToArray() : itemList.OrderBy(item => item.DisplayName).ToArray(), m_size = itemList.Count }; targetList.Refresh(); // If the list is empty, show the 'no props' label; otherwise, hide it. if (targetList.m_rowsData.m_size == 0) { noPropsLabel.Show(); } else { noPropsLabel.Hide(); } }
/* public static void ExportOBJ(){ * TerrainData terrain = Selection.activeGameObject.GetComponent<Terrain>().terrainData; * Vector3 terrainPos = Selection.activeGameObject.transform.position; * string fileName = EditorUtility.SaveFilePanel("Export .obj file", "", "Terrain", "obj"); * int w = terrain.heightmapWidth; * int h = terrain.heightmapHeight; * Vector3 meshScale = terrain.size; * int tRes = (int)Mathf.Pow(2, (int)saveResolution ); * meshScale = new Vector3(meshScale.x / (w - 1) * tRes, meshScale.y, meshScale.z / (h - 1) * tRes); * Vector2 uvScale = new Vector2(1.0f / (w - 1), 1.0f / (h - 1)); * float[,] tData = terrain.GetHeights(0, 0, w, h); * * w = (w - 1) / tRes + 1; * h = (h - 1) / tRes + 1; * Vector3[] tVertices = new Vector3[w * h]; * Vector2[] tUV = new Vector2[w * h]; * * int[] tPolys; * * if (saveFormat == SaveFormat.Triangles) * { * tPolys = new int[(w - 1) * (h - 1) * 6]; * } * else * { * tPolys = new int[(w - 1) * (h - 1) * 4]; * } * * // Build vertices and UVs * for (int y = 0; y < h; y++) * { * for (int x = 0; x < w; x++) * { * tVertices[y * w + x] = Vector3.Scale(meshScale, new Vector3(x, tData[x * tRes, y * tRes], y)) + terrainPos; * tUV[y * w + x] = Vector2.Scale( new Vector2(x * tRes, y * tRes), uvScale); * } * } * * int index = 0; * if (saveFormat == SaveFormat.Triangles) * { * // Build triangle indices: 3 indices into vertex array for each triangle * for (int y = 0; y < h - 1; y++) * { * for (int x = 0; x < w - 1; x++) * { * // For each grid cell output two triangles * tPolys[index++] = (y * w) + x; * tPolys[index++] = ((y + 1) * w) + x; * tPolys[index++] = (y * w) + x + 1; * * tPolys[index++] = ((y + 1) * w) + x; * tPolys[index++] = ((y + 1) * w) + x + 1; * tPolys[index++] = (y * w) + x + 1; * } * } * } * else * { * // Build quad indices: 4 indices into vertex array for each quad * for (int y = 0; y < h - 1; y++) * { * for (int x = 0; x < w - 1; x++) * { * // For each grid cell output one quad * tPolys[index++] = (y * w) + x; * tPolys[index++] = ((y + 1) * w) + x; * tPolys[index++] = ((y + 1) * w) + x + 1; * tPolys[index++] = (y * w) + x + 1; * } * } * } * * // Export to .obj * StreamWriter sw = new StreamWriter(fileName); * EditorUtility.DisplayProgressBar("Exporting","Exporting to OBJ file",0.5f); * * sw.WriteLine("# Unity terrain OBJ File"); * * // Write vertices * System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US"); * for (int i = 0; i < tVertices.Length; i++) * { * StringBuilder sb = new StringBuilder("v ", 20); * // StringBuilder stuff is done this way because it's faster than using the "{0} {1} {2}"etc. format * // Which is important when you're exporting huge terrains. * sb.Append(tVertices[i].x.ToString()).Append(" "). * Append(tVertices[i].y.ToString()).Append(" "). * Append(tVertices[i].z.ToString()); * sw.WriteLine(sb); * } * // Write UVs * for (int i = 0; i < tUV.Length; i++) * { * StringBuilder sb = new StringBuilder("vt ", 22); * sb.Append(tUV[i].x.ToString()).Append(" "). * Append(tUV[i].y.ToString()); * sw.WriteLine(sb); * } * if (saveFormat == SaveFormat.Triangles) * { * // Write triangles * for (int i = 0; i < tPolys.Length; i += 3) * { * StringBuilder sb = new StringBuilder("f ", 43); * sb.Append(tPolys[i] + 1).Append("/").Append(tPolys[i] + 1).Append(" "). * Append(tPolys[i + 1] + 1).Append("/").Append(tPolys[i + 1] + 1).Append(" "). * Append(tPolys[i + 2] + 1).Append("/").Append(tPolys[i + 2] + 1); * sw.WriteLine(sb); * } * } * else * { * // Write quads * for (int i = 0; i < tPolys.Length; i += 4) * { * StringBuilder sb = new StringBuilder("f ", 57); * sb.Append(tPolys[i] + 1).Append("/").Append(tPolys[i] + 1).Append(" "). * Append(tPolys[i + 1] + 1).Append("/").Append(tPolys[i + 1] + 1).Append(" "). * Append(tPolys[i + 2] + 1).Append("/").Append(tPolys[i + 2] + 1).Append(" "). * Append(tPolys[i + 3] + 1).Append("/").Append(tPolys[i + 3] + 1); * sw.WriteLine(sb); * } * } * sw.Close(); * terrain = null; * EditorUtility.ClearProgressBar(); * } */ public static Texture2D ToTexture(GameObject go, int mapType, int layer = 0) { TerrainData terdata = go.GetComponent <Terrain>().terrainData; int res = 0; Texture2D tmpTexture = new Texture2D(terdata.heightmapResolution, terdata.heightmapResolution); Color tmpColor = Color.black; switch (mapType) { case 0: // -- Render Heightmap -- float[,] tmpHeights = terdata.GetHeights(0, 0, terdata.heightmapResolution, terdata.heightmapResolution); for (int hY = 0; hY < terdata.heightmapResolution; hY++) { for (int hX = 0; hX < terdata.heightmapResolution; hX++) { tmpTexture.SetPixel(hX, hY, new Color(tmpHeights[hX, hY], tmpHeights[hX, hY], tmpHeights[hX, hY])); } } break; case 1: // -- Render Splatmap -- res = terdata.alphamapResolution; tmpTexture = new Texture2D(res, res); float[,,] alphadata = terdata.GetAlphamaps(0, 0, res, res); for (int y = 0; y < res; y++) { for (int x = 0; x < res; x++) { if (terdata.splatPrototypes.Length > 1) { tmpColor.r = alphadata[x, y, 1]; } if (terdata.splatPrototypes.Length > 2) { tmpColor.g = alphadata[x, y, 2]; } else { tmpColor.g = 0.0f; } if (terdata.splatPrototypes.Length > 3) { tmpColor.b = alphadata[x, y, 3]; } else { tmpColor.b = 0.0f; } tmpTexture.SetPixel(x, y, tmpColor); } } break; case 2: // -- Detail Layer -- res = terdata.detailResolution; int[,] detaildata = terdata.GetDetailLayer(0, 0, res, res, layer); tmpTexture = new Texture2D(res, res); for (int y = 0; y < res; y++) { for (int x = 0; x < res; x++) { tmpColor.r = (1.0f / 16) * detaildata[x, y]; tmpTexture.SetPixel(x, y, tmpColor); } } break; case 3: // -- Tree Layer -- res = 256; tmpTexture = new Texture2D(res, res); for (int y = 0; y < res; y++) { for (int x = 0; x < res; x++) { tmpTexture.SetPixel(x, y, Color.black); } } for (int tI = 0; tI < terdata.treeInstances.Length; tI++) { TreeInstance treeInst = terdata.treeInstances[tI]; if (treeInst.prototypeIndex == layer) { tmpTexture.SetPixel((int)(treeInst.position.x * res), (int)(treeInst.position.z * res), Color.white); } } break; } tmpTexture.Apply(); return(tmpTexture); }
private void Update() { if (startBushGrowth) // && counter%60 == 0) { float maxDiff = 0; for (int i = 0; i < myTerrain.terrainData.treeInstances.Length; i++) { TreeInstance t = myTerrain.terrainData.GetTreeInstance(i); // if it's a resprouter, compute from resprouters, otherwise read from reseeders float percentage = (t.prototypeIndex == 0) ? (ResprouterSizes[index] - ResprouterSizes[index - 1]) / ResprouterSizes[index - 1] : (ReseederSizes[index] - ReseederSizes[index - 1]) / ReseederSizes[index - 1]; if ((ReseederSizes[index] - ReseederSizes[index - 1]) > maxDiff) { maxDiff = Mathf.Abs(ReseederSizes[index] - ReseederSizes[index - 1]); } float x = t.heightScale * (1 + percentage); //if scale is dropped drastically and there hasn't been a fire yet if (x < 0.65f && !fireOccurred) { fireOccurred = true; if (climate.Equals("dry")) { StartSun(); } else if (climate.Equals("wet")) { StartRain(new Vector3(0, 50, 0)); } else if (climate.Equals("dryandwet")) { StartSun(); StartRain(new Vector3(30, 50, 0)); } StartFire(5); break; // break out of the for loop so rest of the bushes are not read } else { if (fireOccurred && (x > 1f)) //reset Fire Occured status to be false { fireOccurred = false; } t.heightScale = x; t.widthScale = x; myTerrain.terrainData.SetTreeInstance(i, t); } } index++; //read until there are no more Biomasses to read if (index >= 10 * 365)//15 * 365)//ResprouterSizes.Count - 1) { //Debug.Log("maxDiff is: " + maxDiff); myTerrain.GetComponent <TestGenerator>().DestroyBushes(); startBushGrowth = false; SceneMontroller.Instance.ActivateNextButton(scenario); scenario++; } } }
internal void AddTreeToTile(TerrainTile tile, TreeInstance tree, int rand) { tree.position = new Vector3(tile.floatPos.x + Random.Range(0, tilePosJitter), 0f, tile.floatPos.y + Random.Range(0, tilePosJitter)); terrain.AddTreeInstance(tree); tile.TreeInstanceIndex = terrainData.treeInstanceCount - 1; }
void copyTerrain(Terrain origTerrain, string newName, float xMin, float xMax, float zMin, float zMax, int heightmapResolution, int detailResolution, int alphamapResolution) { if (xMin < 0 || xMin > xMax || xMax > origTerrain.terrainData.size.x || zMin < 0 || zMin > zMax || zMax > origTerrain.terrainData.size.z) { return; } TerrainData td = new TerrainData(); GameObject TerGameObeject = Terrain.CreateTerrainGameObject(td); Terrain newTerrain = TerGameObeject.GetComponent <Terrain>(); // Copy over all values newTerrain.bakeLightProbesForTrees = origTerrain.bakeLightProbesForTrees; newTerrain.basemapDistance = origTerrain.basemapDistance; newTerrain.castShadows = origTerrain.castShadows; newTerrain.collectDetailPatches = origTerrain.collectDetailPatches; newTerrain.detailObjectDensity = origTerrain.detailObjectDensity; newTerrain.detailObjectDistance = origTerrain.detailObjectDistance; newTerrain.drawHeightmap = origTerrain.drawHeightmap; newTerrain.drawTreesAndFoliage = origTerrain.drawTreesAndFoliage; newTerrain.editorRenderFlags = origTerrain.editorRenderFlags; newTerrain.heightmapMaximumLOD = origTerrain.heightmapMaximumLOD; newTerrain.heightmapPixelError = origTerrain.heightmapPixelError; newTerrain.legacyShininess = origTerrain.legacyShininess; newTerrain.legacySpecular = origTerrain.legacySpecular; newTerrain.lightmapIndex = origTerrain.lightmapIndex; newTerrain.lightmapScaleOffset = origTerrain.lightmapScaleOffset; newTerrain.materialTemplate = origTerrain.materialTemplate; newTerrain.materialType = origTerrain.materialType; newTerrain.realtimeLightmapIndex = origTerrain.realtimeLightmapIndex; newTerrain.realtimeLightmapScaleOffset = origTerrain.realtimeLightmapScaleOffset; newTerrain.reflectionProbeUsage = origTerrain.reflectionProbeUsage; newTerrain.treeBillboardDistance = origTerrain.treeBillboardDistance; newTerrain.treeCrossFadeLength = origTerrain.treeCrossFadeLength; newTerrain.treeDistance = origTerrain.treeDistance; newTerrain.treeMaximumFullLODCount = origTerrain.treeMaximumFullLODCount; td.splatPrototypes = origTerrain.terrainData.splatPrototypes; td.treePrototypes = origTerrain.terrainData.treePrototypes; td.detailPrototypes = origTerrain.terrainData.detailPrototypes; float xMinNorm = xMin / origTerrain.terrainData.size.x; float xMaxNorm = xMax / origTerrain.terrainData.size.x; float zMinNorm = zMin / origTerrain.terrainData.size.z; float zMaxNorm = zMax / origTerrain.terrainData.size.z; float dimRatio1, dimRatio2; //Set height for split terrain part td.heightmapResolution = heightmapResolution; float[,] newHeights = new float[heightmapResolution, heightmapResolution]; dimRatio1 = (xMax - xMin) / heightmapResolution; dimRatio2 = (zMax - zMin) / heightmapResolution; for (int i = 0; i < heightmapResolution; i++) { for (int j = 0; j < heightmapResolution; j++) { newHeights[j, i] = origTerrain.SampleHeight(new Vector3(xMin + (i * dimRatio1), 0, zMin + (j * dimRatio2))) / origTerrain.terrainData.size.y; } } td.SetHeightsDelayLOD(0, 0, newHeights); td.SetDetailResolution(detailResolution, 8); td.alphamapResolution = alphamapResolution; // Set tree for split terrain part for (int i = 0; i < origTerrain.terrainData.treeInstanceCount; i++) { TreeInstance ti = origTerrain.terrainData.treeInstances[i]; if (ti.position.x < xMinNorm || ti.position.x >= xMaxNorm) { continue; } if (ti.position.z < zMinNorm || ti.position.z >= zMaxNorm) { continue; } ti.position = new Vector3(((ti.position.x * origTerrain.terrainData.size.x) - xMin) / (xMax - xMin), ti.position.y, ((ti.position.z * origTerrain.terrainData.size.z) - zMin) / (zMax - zMin)); newTerrain.AddTreeInstance(ti); } //Set grass for split terrain part for (int layer = 0; layer < origTerrain.terrainData.detailPrototypes.Length; layer++) { int[,] detailLayer = origTerrain.terrainData.GetDetailLayer( Mathf.FloorToInt(xMinNorm * origTerrain.terrainData.detailWidth), Mathf.FloorToInt(zMinNorm * origTerrain.terrainData.detailHeight), Mathf.FloorToInt((xMaxNorm - xMinNorm) * origTerrain.terrainData.detailWidth), Mathf.FloorToInt((zMaxNorm - zMinNorm) * origTerrain.terrainData.detailHeight), layer); int[,] newDetailLayer = new int[detailResolution, detailResolution]; dimRatio1 = (float)detailLayer.GetLength(0) / detailResolution; dimRatio2 = (float)detailLayer.GetLength(1) / detailResolution; for (int i = 0; i < newDetailLayer.GetLength(0); i++) { for (int j = 0; j < newDetailLayer.GetLength(1); j++) { newDetailLayer[i, j] = detailLayer[Mathf.FloorToInt(i * dimRatio1), Mathf.FloorToInt(j * dimRatio2)]; } } td.SetDetailLayer(0, 0, layer, newDetailLayer); } TerGameObeject.transform.position = new Vector3(origTerrain.transform.position.x + xMin, origTerrain.transform.position.y, origTerrain.transform.position.z + zMin); TerGameObeject.name = newName; TerGameObeject.transform.parent = _terGen.transform; td.size = new Vector3(xMax - xMin, origTerrain.terrainData.size.y, zMax - zMin); }
protected override void Init() { if (splatSettings == null || splatSettings.Length == 0) { splatSettings = new TerrainVoxelDefinitionMapping[64]; } if (detailSettings == null || detailSettings.Length == 0) { detailSettings = new VegetationVoxelDefinitionMapping[64]; } if (treeSettings == null || treeSettings.Length == 0) { treeSettings = new TerrainModelDefinitionMapping[32]; } if (detailLayers == null || detailLayers.Length < 32) { detailLayers = new DetailLayerInfo[32]; } if (waterVoxel == null) { waterVoxel = Resources.Load <VoxelDefinition> ("VoxelPlay/Defaults/Water/VoxelWaterSea"); } env.AddVoxelDefinition(bedrockVoxel); #if UNITY_EDITOR if (world != null && world.terrainGenerator == null) { world.terrainGenerator = this; } if (terrainData == null) { Terrain activeTerrain = Terrain.activeTerrain; if (activeTerrain != null) { terrainData = activeTerrain.terrainData; ExamineTerrainData(); } } #endif if (terrainData == null) { return; } if (lastTerrainDataLoaded != null && lastTerrainDataLoaded == terrainData && heights != null && heights.Length > 0) { return; } lastTerrainDataLoaded = terrainData; maxHeight = terrainData.size.y; int th = terrainData.heightmapHeight; int tw = terrainData.heightmapWidth; int len = tw * th; if (heights == null || heights.Length != len) { heights = new TerrainHeightInfo[len]; } float[,,] heightInfo = terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight); int detailLayerCount = terrainData.detailPrototypes.Length; for (int d = 0; d < detailLayerCount; d++) { detailLayers [d].detailLayer = terrainData.GetDetailLayer(0, 0, terrainData.detailWidth, terrainData.detailHeight, d); } int i = 0; int alphaMapsLayerCount = heightInfo.GetUpperBound(2); int currentDetailLayer = 0; int vegDensity = (int)(16 * (1f - vegetationDensity)); for (int y = 0; y < th; y++) { int alphamapY = y * terrainData.alphamapHeight / th; int detailY = y * terrainData.detailHeight / th; for (int x = 0; x < tw; x++, i++) { int alphamapX = x * terrainData.alphamapWidth / tw; heights [i].altitude = terrainData.GetHeight(x, y) / maxHeight; float maxBlend = -1; for (int a = 0; a <= alphaMapsLayerCount; a++) { float alphamapValue = heightInfo [alphamapY, alphamapX, a]; if (alphamapValue > maxBlend) { maxBlend = alphamapValue; heights [i].terrainVoxelTop = splatSettings [a].top; heights [i].terrainVoxelDirt = splatSettings [a].dirt; if (maxBlend >= 1f) { break; } } } if (detailLayerCount > 0) { for (int v = 0; v < detailLayerCount; v++) { currentDetailLayer++; if (currentDetailLayer >= detailLayerCount) { currentDetailLayer = 0; } if (detailSettings [currentDetailLayer].vd != null) { int detailX = x * terrainData.detailWidth / tw; int o = detailLayers [currentDetailLayer].detailLayer [detailY, detailX]; if (o > vegDensity) { heights [i].vegetationVoxel = detailSettings [currentDetailLayer].vd; break; } } } } } } float sx = terrainData.size.x; float sz = terrainData.size.z; for (int t = 0; t < terrainData.treeInstances.Length; t++) { TreeInstance ti = terrainData.treeInstances [t]; int hindex = GetHeightIndex(ti.position.x * sx - sx / 2, ti.position.z * sz - sz / 2); heights [hindex].treeModel = treeSettings [ti.prototypeIndex].md; } }
public static void SectorizeTerrain(Terrain terrain, int sectorsWidth, int sectorsLength, int sectorsHeight, bool splitTerrain, bool createPortalGeo, bool includeStatic, bool includeDynamic) { if (!terrain) { Debug.LogWarning("Cannot sectorize null terrain."); return; } if (terrain.transform.root.GetComponentsInChildren <SECTR_Sector>().Length > 0) { Debug.LogWarning("Cannot sectorize terrain that is already part of a Sector."); } string undoString = "Sectorized " + terrain.name; if (sectorsWidth == 1 && sectorsLength == 1) { SECTR_Sector newSector = terrain.gameObject.AddComponent <SECTR_Sector>(); SECTR_Undo.Created(newSector, undoString); newSector.ForceUpdate(true); return; } if (splitTerrain && (!Mathf.IsPowerOfTwo(sectorsWidth) || !Mathf.IsPowerOfTwo(sectorsLength))) { Debug.LogWarning("Splitting terrain requires power of two sectors in width and length."); splitTerrain = false; } else if (splitTerrain && sectorsWidth != sectorsLength) { Debug.LogWarning("Splitting terrain requires same number of sectors in width and length."); splitTerrain = false; } int terrainLayer = terrain.gameObject.layer; Vector3 terrainSize = terrain.terrainData.size; float sectorWidth = terrainSize.x / sectorsWidth; float sectorHeight = terrainSize.y / sectorsHeight; float sectorLength = terrainSize.z / sectorsLength; int heightmapWidth = (terrain.terrainData.heightmapWidth / sectorsWidth); int heightmapLength = (terrain.terrainData.heightmapHeight / sectorsLength); int alphaWidth = terrain.terrainData.alphamapWidth / sectorsWidth; int alphaLength = terrain.terrainData.alphamapHeight / sectorsLength; int detailWidth = terrain.terrainData.detailWidth / sectorsWidth; int detailLength = terrain.terrainData.detailHeight / sectorsLength; string sceneDir = ""; string sceneName = ""; string exportFolder = splitTerrain ? SECTR_Asset.MakeExportFolder("TerrainSplits", false, out sceneDir, out sceneName) : ""; Transform baseTransform = null; if (splitTerrain) { GameObject baseObject = new GameObject(terrain.name); baseTransform = baseObject.transform; SECTR_Undo.Created(baseObject, undoString); } List <Transform> rootTransforms = new List <Transform>(); List <Bounds> rootBounds = new List <Bounds>(); _GetRoots(includeStatic, includeDynamic, rootTransforms, rootBounds); // Create Sectors string progressTitle = "Sectorizing Terrain"; int progressCounter = 0; EditorUtility.DisplayProgressBar(progressTitle, "Preparing", 0); SECTR_Sector[,,] newSectors = new SECTR_Sector[sectorsWidth, sectorsLength, sectorsHeight]; Terrain[,] newTerrains = splitTerrain ? new Terrain[sectorsWidth, sectorsLength] : null; for (int widthIndex = 0; widthIndex < sectorsWidth; ++widthIndex) { for (int lengthIndex = 0; lengthIndex < sectorsLength; ++lengthIndex) { for (int heightIndex = 0; heightIndex < sectorsHeight; ++heightIndex) { string newName = terrain.name + " " + widthIndex + "-" + lengthIndex + "-" + heightIndex; EditorUtility.DisplayProgressBar(progressTitle, "Creating sector " + newName, progressCounter++ / (float)(sectorsWidth * sectorsLength * sectorsHeight)); GameObject newSectorObject = new GameObject("SECTR " + newName + " Sector"); newSectorObject.transform.parent = baseTransform; Vector3 sectorCorner = new Vector3(widthIndex * sectorWidth, heightIndex * sectorHeight, lengthIndex * sectorLength) + terrain.transform.position; newSectorObject.transform.position = sectorCorner; newSectorObject.isStatic = true; SECTR_Sector newSector = newSectorObject.AddComponent <SECTR_Sector>(); newSector.OverrideBounds = !splitTerrain && (sectorsWidth > 1 || sectorsLength > 1); newSector.BoundsOverride = new Bounds(sectorCorner + new Vector3(sectorWidth * 0.5f, sectorHeight * 0.5f, sectorLength * 0.5f), new Vector3(sectorWidth, sectorHeight, sectorLength)); newSectors[widthIndex, lengthIndex, heightIndex] = newSector; if (splitTerrain && heightIndex == 0) { GameObject newTerrainObject = new GameObject(newName + " Terrain"); newTerrainObject.layer = terrainLayer; newTerrainObject.tag = terrain.tag; newTerrainObject.transform.parent = newSectorObject.transform; newTerrainObject.transform.localPosition = Vector3.zero; newTerrainObject.transform.localRotation = Quaternion.identity; newTerrainObject.transform.localScale = Vector3.one; newTerrainObject.isStatic = true; Terrain newTerrain = newTerrainObject.AddComponent <Terrain>(); newTerrain.terrainData = SECTR_Asset.Create <TerrainData>(exportFolder, newName, new TerrainData()); EditorUtility.SetDirty(newTerrain.terrainData); SECTR_VC.WaitForVC(); // Copy properties // Basic terrain properties newTerrain.editorRenderFlags = terrain.editorRenderFlags; newTerrain.castShadows = terrain.castShadows; newTerrain.heightmapMaximumLOD = terrain.heightmapMaximumLOD; newTerrain.heightmapPixelError = terrain.heightmapPixelError; newTerrain.lightmapIndex = -1; // Can't set lightmap UVs on terrain. newTerrain.materialTemplate = terrain.materialTemplate; #if !UNITY_4 newTerrain.bakeLightProbesForTrees = terrain.bakeLightProbesForTrees; newTerrain.legacyShininess = terrain.legacyShininess; newTerrain.legacySpecular = terrain.legacySpecular; #endif // Copy geometric data int heightmapBaseX = widthIndex * heightmapWidth; int heightmapBaseY = lengthIndex * heightmapLength; int heightmapWidthX = heightmapWidth + (sectorsWidth > 1 ? 1 : 0); int heightmapWidthY = heightmapLength + (sectorsLength > 1 ? 1 : 0); newTerrain.terrainData.heightmapResolution = terrain.terrainData.heightmapResolution / sectorsWidth; newTerrain.terrainData.size = new Vector3(sectorWidth, terrainSize.y, sectorLength); newTerrain.terrainData.SetHeights(0, 0, terrain.terrainData.GetHeights(heightmapBaseX, heightmapBaseY, heightmapWidthX, heightmapWidthY)); #if !UNITY_4 newTerrain.terrainData.thickness = terrain.terrainData.thickness; #endif // Copy alpha maps int alphaBaseX = alphaWidth * widthIndex; int alphaBaseY = alphaLength * lengthIndex; newTerrain.terrainData.splatPrototypes = terrain.terrainData.splatPrototypes; newTerrain.basemapDistance = terrain.basemapDistance; newTerrain.terrainData.baseMapResolution = terrain.terrainData.baseMapResolution / sectorsWidth; newTerrain.terrainData.alphamapResolution = terrain.terrainData.alphamapResolution / sectorsWidth; newTerrain.terrainData.SetAlphamaps(0, 0, terrain.terrainData.GetAlphamaps(alphaBaseX, alphaBaseY, alphaWidth, alphaLength)); // Copy detail info newTerrain.detailObjectDensity = terrain.detailObjectDensity; newTerrain.detailObjectDistance = terrain.detailObjectDistance; newTerrain.terrainData.detailPrototypes = terrain.terrainData.detailPrototypes; newTerrain.terrainData.SetDetailResolution(terrain.terrainData.detailResolution / sectorsWidth, 8); // TODO: extract detailResolutionPerPatch #if !UNITY_4 newTerrain.collectDetailPatches = terrain.collectDetailPatches; #endif int detailBaseX = detailWidth * widthIndex; int detailBaseY = detailLength * lengthIndex; int numLayers = terrain.terrainData.detailPrototypes.Length; for (int layer = 0; layer < numLayers; ++layer) { newTerrain.terrainData.SetDetailLayer(0, 0, layer, terrain.terrainData.GetDetailLayer(detailBaseX, detailBaseY, detailWidth, detailLength, layer)); } // Copy grass and trees newTerrain.terrainData.wavingGrassAmount = terrain.terrainData.wavingGrassAmount; newTerrain.terrainData.wavingGrassSpeed = terrain.terrainData.wavingGrassSpeed; newTerrain.terrainData.wavingGrassStrength = terrain.terrainData.wavingGrassStrength; newTerrain.terrainData.wavingGrassTint = terrain.terrainData.wavingGrassTint; newTerrain.treeBillboardDistance = terrain.treeBillboardDistance; newTerrain.treeCrossFadeLength = terrain.treeCrossFadeLength; newTerrain.treeDistance = terrain.treeDistance; newTerrain.treeMaximumFullLODCount = terrain.treeMaximumFullLODCount; newTerrain.terrainData.treePrototypes = terrain.terrainData.treePrototypes; newTerrain.terrainData.RefreshPrototypes(); foreach (TreeInstance treeInstace in terrain.terrainData.treeInstances) { if (treeInstace.prototypeIndex >= 0 && treeInstace.prototypeIndex < newTerrain.terrainData.treePrototypes.Length && newTerrain.terrainData.treePrototypes[treeInstace.prototypeIndex].prefab) { Vector3 worldSpaceTreePos = Vector3.Scale(treeInstace.position, terrainSize) + terrain.transform.position; if (newSector.BoundsOverride.Contains(worldSpaceTreePos)) { Vector3 localSpaceTreePos = new Vector3((worldSpaceTreePos.x - newTerrain.transform.position.x) / sectorWidth, treeInstace.position.y, (worldSpaceTreePos.z - newTerrain.transform.position.z) / sectorLength); TreeInstance newInstance = treeInstace; newInstance.position = localSpaceTreePos; newTerrain.AddTreeInstance(newInstance); } } } // Copy physics #if UNITY_4_LATE newTerrain.terrainData.physicMaterial = terrain.terrainData.physicMaterial; #endif // Force terrain to rebuild newTerrain.Flush(); UnityEditor.EditorUtility.SetDirty(newTerrain.terrainData); SECTR_VC.WaitForVC(); newTerrain.enabled = false; newTerrain.enabled = true; TerrainCollider terrainCollider = terrain.GetComponent <TerrainCollider>(); if (terrainCollider) { TerrainCollider newCollider = newTerrainObject.AddComponent <TerrainCollider>(); #if !UNITY_4_LATE newCollider.sharedMaterial = terrainCollider.sharedMaterial; #endif newCollider.terrainData = newTerrain.terrainData; } newTerrains[widthIndex, lengthIndex] = newTerrain; SECTR_Undo.Created(newTerrainObject, undoString); } newSector.ForceUpdate(true); SECTR_Undo.Created(newSectorObject, undoString); _Encapsulate(newSector, rootTransforms, rootBounds, undoString); } } } // Create portals and neighbors progressCounter = 0; for (int widthIndex = 0; widthIndex < sectorsWidth; ++widthIndex) { for (int lengthIndex = 0; lengthIndex < sectorsLength; ++lengthIndex) { for (int heightIndex = 0; heightIndex < sectorsHeight; ++heightIndex) { EditorUtility.DisplayProgressBar(progressTitle, "Creating portals...", progressCounter++ / (float)(sectorsWidth * sectorsLength * sectorsHeight)); if (widthIndex < sectorsWidth - 1) { _CreatePortal(createPortalGeo, newSectors[widthIndex + 1, lengthIndex, heightIndex], newSectors[widthIndex, lengthIndex, heightIndex], baseTransform, undoString); } if (lengthIndex < sectorsLength - 1) { _CreatePortal(createPortalGeo, newSectors[widthIndex, lengthIndex + 1, heightIndex], newSectors[widthIndex, lengthIndex, heightIndex], baseTransform, undoString); } if (heightIndex > 0) { _CreatePortal(createPortalGeo, newSectors[widthIndex, lengthIndex, heightIndex], newSectors[widthIndex, lengthIndex, heightIndex - 1], baseTransform, undoString); } } } } if (splitTerrain) { progressCounter = 0; for (int widthIndex = 0; widthIndex < sectorsWidth; ++widthIndex) { for (int lengthIndex = 0; lengthIndex < sectorsLength; ++lengthIndex) { EditorUtility.DisplayProgressBar(progressTitle, "Smoothing split terrain...", progressCounter++ / (float)(sectorsWidth * sectorsLength * sectorsHeight)); // Blend together the seams of the alpha maps, which requires // going through all of the mip maps of all of the layer textures. // We have to blend here rather than when we set the alpha data (above) // because Unity computes mips and we need to blend all of the mips. Terrain newTerrain = newTerrains[widthIndex, lengthIndex]; SECTR_Sector terrainSector = newSectors[widthIndex, lengthIndex, 0]; terrainSector.LeftTerrain = widthIndex > 0 ? newSectors[widthIndex - 1, lengthIndex, 0] : null; terrainSector.RightTerrain = widthIndex < sectorsWidth - 1 ? newSectors[widthIndex + 1, lengthIndex, 0] : null; terrainSector.BottomTerrain = lengthIndex > 0 ? newSectors[widthIndex, lengthIndex - 1, 0] : null; terrainSector.TopTerrain = lengthIndex < sectorsLength - 1 ? newSectors[widthIndex, lengthIndex + 1, 0] : null; terrainSector.ConnectTerrainNeighbors(); // Use reflection trickery to get at the raw texture values. System.Reflection.PropertyInfo alphamapProperty = newTerrain.terrainData.GetType().GetProperty("alphamapTextures", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static); // Get the texture we'll write into Texture2D[] alphaTextures = (Texture2D[])alphamapProperty.GetValue(newTerrain.terrainData, null); int numTextures = alphaTextures.Length; // Get the textures we'll read from Texture2D[] leftNeighborTextures = terrainSector.LeftTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex - 1, lengthIndex].terrainData, null) : null; Texture2D[] rightNeighborTextures = terrainSector.RightTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex + 1, lengthIndex].terrainData, null) : null; Texture2D[] topNeighborTextures = terrainSector.TopTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex, lengthIndex + 1].terrainData, null) : null; Texture2D[] bottomNeighborTextures = terrainSector.BottomTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex, lengthIndex - 1].terrainData, null) : null; for (int textureIndex = 0; textureIndex < numTextures; ++textureIndex) { Texture2D alphaTexture = alphaTextures[textureIndex]; Texture2D leftTexture = leftNeighborTextures != null ? leftNeighborTextures[textureIndex] : null; Texture2D rightTexture = rightNeighborTextures != null ? rightNeighborTextures[textureIndex] : null; Texture2D topTexture = topNeighborTextures != null ? topNeighborTextures[textureIndex] : null; Texture2D bottomTexture = bottomNeighborTextures != null ? bottomNeighborTextures[textureIndex] : null; int numMips = alphaTexture.mipmapCount; for (int mipIndex = 0; mipIndex < numMips; ++mipIndex) { Color[] alphaTexels = alphaTexture.GetPixels(mipIndex); int width = (int)Mathf.Sqrt(alphaTexels.Length); int height = width; for (int texelWidthIndex = 0; texelWidthIndex < width; ++texelWidthIndex) { for (int texelHeightIndex = 0; texelHeightIndex < height; ++texelHeightIndex) { // We can take advantage of the build order to average on the leading edges (right and top) // and then copy form the trailing edges (left and bottom) if (texelWidthIndex == 0 && leftTexture) { Color[] neighborTexels = leftTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] = neighborTexels[(width - 1) + (texelHeightIndex * width)]; } else if (texelWidthIndex == width - 1 && rightTexture) { Color[] neighborTexels = rightTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] += neighborTexels[0 + (texelHeightIndex * width)]; alphaTexels[texelWidthIndex + texelHeightIndex * width] *= 0.5f; } else if (texelHeightIndex == 0 && bottomTexture) { Color[] neighborTexels = bottomTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] = neighborTexels[texelWidthIndex + ((height - 1) * width)]; } else if (texelHeightIndex == height - 1 && topTexture) { Color[] neighborTexels = topTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] += neighborTexels[texelWidthIndex + (0 * width)]; alphaTexels[texelWidthIndex + texelHeightIndex * width] *= 0.5f; } } } alphaTexture.SetPixels(alphaTexels, mipIndex); } alphaTexture.wrapMode = TextureWrapMode.Clamp; alphaTexture.Apply(false); } newTerrain.Flush(); } } } EditorUtility.ClearProgressBar(); // destroy original terrain if (splitTerrain) { SECTR_Undo.Destroy(terrain.gameObject, undoString); } }
internal void AddTree(out TreeInstance tree){}
public void GenerateTrees(Terrain terrain) { var data = terrain.terrainData; var num = data.treePrototypes.Length; if (num == 0) { Debug.LogWarning("Can't place trees because no prototypes are defined. Process aborted."); return; } //Undo.RegisterCompleteObjectUndo (data, "Mass Place Trees"); float timeElapsed = 0; var array = new TreeInstance[count]; var i = 0; while (i < array.Length) { //stop if process have run for over X seconds timeElapsed += Time.deltaTime; if (timeElapsed >= maxTime) { Debug.LogWarning("Process was taking too much time to run"); return; } var position = new Vector3(Random.value, 0.0f, Random.value); // don't allow placement of trees below minWorldY and above maxWorldY var y = data.GetInterpolatedHeight(position.x, position.z); var worldY = y + terrain.transform.position.y; if (worldY < minWorldY || worldY > maxWorldY) { continue; } // don't allow placement of trees on surfaces flatter than minSlope and steeper than maxSlope var steepness = data.GetSteepness(position.x, position.z); if (steepness < minSlope || steepness > maxSlope) { continue; } var color = Color.Lerp(Color.white, Color.gray * 0.7f, Random.value); color.a = 1f; var treeInstance = default(TreeInstance); treeInstance.position = position; treeInstance.color = color; treeInstance.lightmapColor = Color.white; treeInstance.prototypeIndex = Random.Range(0, num); treeInstance.widthScale = Random.Range(minWidthScale, maxWidthScale); treeInstance.heightScale = Random.Range(minHeightScale, maxHeightScale); array [i] = treeInstance; i++; } data.treeInstances = array; //RecalculateTreePositions(data); terrain.Flush(); }
private static void Slicing() { Terrain terrain = GameObject.FindObjectOfType <Terrain>(); if (terrain == null) { Debug.LogError("找不到地形!"); return; } string savepath = "Assets/Resources/" + terrain.name + "/"; if (Directory.Exists(savepath)) { Directory.Delete(savepath, true); } Directory.CreateDirectory(savepath); TerrainData terrainData = terrain.terrainData; Vector3 oldSize = terrainData.size; Vector3 oldPos = terrain.transform.position; oldPos = new Vector3(oldPos.x - oldSize.x, oldPos.y, oldPos.z); //得到新地图分辨率 int newAlphamapResolution = terrainData.alphamapResolution / trnconst.SLICE; SplatPrototype[] splatProtos = terrainData.splatPrototypes; var detailProtos = terrainData.detailPrototypes; var treeProtos = terrainData.treePrototypes; var treeInst = terrainData.treeInstances; var grassStrength = terrainData.wavingGrassStrength; var grassAmount = terrainData.wavingGrassAmount; var grassSpeed = terrainData.wavingGrassSpeed; var grassTint = terrainData.wavingGrassTint; int terrainsWide = trnconst.SLICE; int terrainsLong = trnconst.SLICE; int newDetailResolution = terrainData.detailResolution / trnconst.SLICE; int resolutionPerPatch = 8; //设置高度 int xBase = terrainData.heightmapWidth / terrainsWide; int yBase = terrainData.heightmapHeight / terrainsLong; TerrainData[] data = new TerrainData[terrainsWide * terrainsLong]; Dictionary <int, List <TreeInstance> > map = new Dictionary <int, List <TreeInstance> >(); int arrayPos = 0; try { //循环宽和长,生成小块地形 for (int x = 0; x < terrainsWide; ++x) { for (int y = 0; y < terrainsLong; ++y) { //创建资源 TerrainData newData = new TerrainData(); map[arrayPos] = new List <TreeInstance>(); data[arrayPos++] = newData; string terrainName = terrain.name + trnconst.sep + y + trnconst.sep + x + ".asset"; AssetDatabase.CreateAsset(newData, savepath + terrainName); EditorUtility.DisplayProgressBar("正在分割地形", terrainName, (float)(x * terrainsWide + y) / (float)(terrainsWide * terrainsLong)); //设置分辨率参数 newData.heightmapResolution = (terrainData.heightmapResolution - 1) / trnconst.SLICE; newData.alphamapResolution = terrainData.alphamapResolution / trnconst.SLICE; newData.baseMapResolution = terrainData.baseMapResolution / trnconst.SLICE; //设置大小 newData.size = new Vector3(oldSize.x / terrainsWide, oldSize.y, oldSize.z / terrainsLong); //设置地形原型 SplatPrototype[] newSplats = new SplatPrototype[splatProtos.Length]; for (int i = 0; i < splatProtos.Length; ++i) { newSplats[i] = new SplatPrototype(); newSplats[i].texture = splatProtos[i].texture; newSplats[i].tileSize = splatProtos[i].tileSize; float offsetX = (newData.size.x * x) % splatProtos[i].tileSize.x + splatProtos[i].tileOffset.x; float offsetY = (newData.size.z * y) % splatProtos[i].tileSize.y + splatProtos[i].tileOffset.y; newSplats[i].tileOffset = new Vector2(offsetX, offsetY); } newData.splatPrototypes = newSplats; //设置混合贴图 float[,,] alphamap = new float[newAlphamapResolution, newAlphamapResolution, splatProtos.Length]; alphamap = terrainData.GetAlphamaps(x * newData.alphamapWidth, y * newData.alphamapHeight, newData.alphamapWidth, newData.alphamapHeight); newData.SetAlphamaps(0, 0, alphamap); float[,] height = terrainData.GetHeights(xBase * x, yBase * y, xBase + 1, yBase + 1); newData.SetHeights(0, 0, height); newData.SetDetailResolution(newDetailResolution, resolutionPerPatch); int[] layers = terrainData.GetSupportedLayers(x * newData.detailWidth - 1, y * newData.detailHeight - 1, newData.detailWidth, newData.detailHeight); int layerLength = layers.Length; DetailPrototype[] tempDetailProtos = new DetailPrototype[layerLength]; for (int i = 0; i < layerLength; i++) { tempDetailProtos[i] = detailProtos[layers[i]]; } newData.detailPrototypes = tempDetailProtos; for (int i = 0; i < layerLength; i++) { newData.SetDetailLayer(0, 0, i, terrainData.GetDetailLayer(x * newData.detailWidth, y * newData.detailHeight, newData.detailWidth, newData.detailHeight, layers[i])); } newData.wavingGrassStrength = grassStrength; newData.wavingGrassAmount = grassAmount; newData.wavingGrassSpeed = grassSpeed; newData.wavingGrassTint = grassTint; newData.treePrototypes = treeProtos; } } int newWidth = (int)oldSize.x / terrainsWide; int newLength = (int)oldSize.z / terrainsLong; for (int i = 0; i < treeInst.Length; i++) { Vector3 origPos = Vector3.Scale(new Vector3(oldSize.x, 1, oldSize.z), new Vector3(treeInst[i].position.x, treeInst[i].position.y, treeInst[i].position.z)); int column = Mathf.FloorToInt(origPos.x / newWidth); int row = Mathf.FloorToInt(origPos.z / newLength); Vector3 tempVect = new Vector3((origPos.x - newWidth * column) / newWidth, origPos.y, (origPos.z - newLength * row) / newWidth); TreeInstance tempTree = new TreeInstance(); tempTree.position = tempVect; tempTree.widthScale = treeInst[i].widthScale; tempTree.heightScale = treeInst[i].heightScale; tempTree.color = treeInst[i].color; tempTree.rotation = treeInst[i].rotation; tempTree.lightmapColor = treeInst[i].lightmapColor; int indx = (column * terrainsWide) + row; tempTree.prototypeIndex = 0; map[indx].Add(tempTree); } for (int i = 0; i < terrainsWide * terrainsLong; i++) { data[i].treeInstances = map[i].ToArray(); data[i].RefreshPrototypes(); } WriteTerrainInfo(terrain, savepath + terrain.name + ".bytes"); } catch (System.Exception e) { Debug.LogError(e.Message); Debug.LogError(e.StackTrace); } finally { EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(); } }
public Vector3 getObjectLoc(TreeInstance obj) { return(new Vector3(obj.position.x * heightmap_width, obj.position.y * terrain_data.heightmapScale.y, obj.position.z * heightmap_height)); }
#pragma warning restore 414 /// <summary> /// Load the trees in from the terrain /// </summary> public void LoadTreesFromTerrain() { //Destroy previous contents m_terrainTrees = null; m_terrainTreeLocations = null; //Work out the bounds of the environment float minY = float.NaN; float minX = float.NaN; float maxX = float.NaN; float minZ = float.NaN; float maxZ = float.NaN; Terrain sampleTerrain = null; foreach (Terrain terrain in Terrain.activeTerrains) { if (float.IsNaN(minY)) { sampleTerrain = terrain; minY = terrain.transform.position.y; minX = terrain.transform.position.x; minZ = terrain.transform.position.z; maxX = minX + terrain.terrainData.size.x; maxZ = minZ + terrain.terrainData.size.z; } else { if (terrain.transform.position.x < minX) { minX = terrain.transform.position.x; } if (terrain.transform.position.z < minZ) { minZ = terrain.transform.position.z; } if ((terrain.transform.position.x + terrain.terrainData.size.x) > maxX) { maxX = terrain.transform.position.x + terrain.terrainData.size.x; } if ((terrain.transform.position.z + terrain.terrainData.size.z) > maxZ) { maxZ = terrain.transform.position.z + terrain.terrainData.size.z; } } } if (sampleTerrain != null) { Rect terrainBounds = new Rect(minX, minZ, maxX - minX, maxZ - minZ); m_terrainTreeLocations = new Quadtree <TreeStruct>(terrainBounds, 32); m_terrainTrees = new List <TreePrototype>(sampleTerrain.terrainData.treePrototypes); foreach (Terrain terrain in Terrain.activeTerrains) { float terrainOffsetX = terrain.transform.position.x; float terrainOffsetY = terrain.transform.position.y; float terrainOffsetZ = terrain.transform.position.z; float terrainWidth = terrain.terrainData.size.x; float terrainHeight = terrain.terrainData.size.y; float terrainDepth = terrain.terrainData.size.z; TreeInstance[] terrainTreeInstances = terrain.terrainData.treeInstances; for (int treeIdx = 0; treeIdx < terrainTreeInstances.Length; treeIdx++) { TreeInstance treeInstance = terrainTreeInstances[treeIdx]; TreeStruct newTree = new TreeStruct(); newTree.position = new Vector3(terrainOffsetX + (treeInstance.position.x * terrainWidth), terrainOffsetY + (treeInstance.position.y * terrainHeight), terrainOffsetZ + (treeInstance.position.z * terrainDepth)); newTree.prototypeID = terrainTreeInstances[treeIdx].prototypeIndex; m_terrainTreeLocations.Insert(newTree.position.x, newTree.position.z, newTree); } } } }
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 override void draw(float x, float z) { var random = new System.Random(); int tot = terrain.getObjectCount(); List <TreeInstance> curr = new List <TreeInstance>(); List <Vector3> currp = new List <Vector3>(); for (int i = 0; i < tot; i++) { TreeInstance o = terrain.getObject(i); Vector3 v = terrain.getObjectLoc(i); if (v.x <= x + radius && v.x >= x - radius && v.z <= z + radius && v.z >= z - radius) { curr.Add(o); currp.Add(v); } } int left = maxObjects - curr.Count; left = maxObjects; if (left > 0) { Array values = Enum.GetValues(typeof(TreeTextures)); for (int i = 0; i < left; i++) { float xi = ((float)random.NextDouble()) * 2.0f - 1; float zi = ((float)random.NextDouble()) * 2.0f - 1; xi = xi * radius; zi = zi * radius; Vector3 p = givePos(x, z); for (int j = 0; j < currp.Count; j++) { Vector3 d = (p - currp[j]); float D = d.sqrMagnitude; if (D <= min_distance) { p = p + (d * (D + 0.01f)); } } if (p.x <= x + radius && p.x >= x - radius && p.z <= z + radius && p.z >= z - radius) { if (zone == Zone.Random) { Zone obs = ((Zone)values.GetValue(random.Next(values.Length))); if (obs == Zone.Random) { obs = Zone.FootHill; } zone = obs; } VegetationConstraint v = terrain.getVegetationFromZone(zone); int index = random.Next(0, v.objects.Length); if (v.objects == null || v.objects.Length == 0) { return; } terrain.object_prefab = v.objects[index]; setPrefab(terrain.registerPrefab(terrain.object_prefab)); if (v.place(p.x, p.z, ref terrain, true)) { currp.Add(p); spawnObject(p.x, p.z); } } //print("---" + i + 'p'+GetType()); //terrain.object_prefab; //getPrefable, terrain.getObject, terrain.countexistingObejects, check current object index } } }
public OverwatchTreeWrapper(ushort treeInstanceId, TreeInstance treeInstance) { TreeId = treeInstanceId; SourcePackageId = ParsePackageId(treeInstance); TechnicalName = treeInstance.Info.name; }