Custom Object2Terrain script creates "curtains" on the edges.
Howdy,
I'm working on a custom version of the Object2Terrain.cs script that's posted on the unify wiki. I've got a large number of terrain chunks I want to generate, so I'm adjusting it to accept a parent object and make a terrain object for each child of that object. However, both my version and the original create these strange artifacts at the edges of the terrain I want to generate.
I've attempted adjusting the bounds that determines where the rays get cast from, but that hasn't fixed the issue. My only guess is that I'm somehow running into a floating point precision issue and the rays are being cast off the side of the mesh, which is why I tried to shrink that box just a little so it would grab the height from just inside the bounds of the mesh. The meshes are only 250 sq. m so I'm not sure how correct this is.
Could anyone help me figure out how to prevent this?
public class Objects2Terrain : EditorWindow
{
[MenuItem("Terrain/Objects to Terrain", false, 2002)]
static void OpenWindow()
{
EditorWindow.GetWindow<Objects2Terrain>();
}
private string PrefabDataAddress = "Assets/Terrain/Prefabs/";
private string TerrainDataAddress = "Assets/Terrain/Prefabs/TerrainData/";
private Object worldMap;
private int TerrainResolution = 512;
private float ChunkProgress;
private void OnGUI()
{
PrefabDataAddress = EditorGUILayout.TextField("PrefabDataAddress", PrefabDataAddress);
TerrainDataAddress = EditorGUILayout.TextField("Terrain Data Address", TerrainDataAddress);
worldMap = EditorGUILayout.ObjectField(worldMap, typeof(GameObject), true);
TerrainResolution = EditorGUILayout.IntField("Terrain Resolution", TerrainResolution );
EditorGUILayout.HelpBox("explanation text here.", MessageType.Info);
if (GUILayout.Button("Make Terrains"))
{
float start = Time.realtimeSinceStartup;
List<GameObject> Chunks = MakeTerrainObjects((GameObject)worldMap, TerrainResolution, TerrainDataAddress);
Debug.Log("Terrain Time: " + (Time.realtimeSinceStartup - start) + " ms");
}
}
List<GameObject> MakeTerrainObjects(GameObject ParentObject, int resolution, string TerrainAddress)
{
List<GameObject> Chunks = new List<GameObject>();
int childCount = ParentObject.transform.childCount;
ChunkProgress = ((1/childCount));
for (int i = 0; i < childCount; i++)
{
GameObject child = ParentObject.transform.GetChild(i).gameObject;
string ChunkName = child.name;
GameObject Chunk = CreateTerrain(child, resolution, TerrainAddress);
Chunk.name = ChunkName;
MeshCollider collider = child.AddComponent<MeshCollider>();
Bounds bounds = collider.bounds;
DestroyImmediate(collider);
Chunk.transform.position = child.transform.position - new Vector3(bounds.extents.x,
child.transform.position.y - bounds.min.y, bounds.extents.z);
Chunks.Add(Chunk);
ChunkProgress = ((i + 1) / childCount);
//Debug.Log(ChunkProgress);
}
return Chunks;
}
delegate void CleanUp();
GameObject CreateTerrain(GameObject meshObject, int resolution, string Address)
{
ShowProgressBar(0.01f, ChunkProgress);
TerrainData terrain = new TerrainData();
terrain.heightmapResolution = resolution;
GameObject terrainObject = Terrain.CreateTerrainGameObject(terrain);
//Set TerrainObject name
string oName = meshObject.name;
terrainObject.name = oName;
Undo.RegisterCreatedObjectUndo(terrainObject, "Object to Terrain");
MeshCollider collider = meshObject.GetComponent<MeshCollider>();
CleanUp cleanUp = null;
//Add a collider to the source object if it doesn't exist
//needed for raycasting
if (!collider)
{
collider = meshObject.AddComponent<MeshCollider>();
cleanUp = () => DestroyImmediate(collider);
}
Bounds bounds = collider.bounds;
terrain.size = collider.bounds.size;// + new Vector3(-0.0001f, 0f, -0.0001f);
//Adjust bounds size to attempt to fix curtain
bounds.size = new Vector3(terrain.size.x - 0.0002f, collider.bounds.size.y, terrain.size.z - 0.0002f);
// Do raycasting samples over the object to see what terrain heights should be
float[,] heights = new float[terrain.heightmapResolution, terrain.heightmapResolution];
Ray ray = new Ray(new Vector3(bounds.min.x, bounds.max.y + bounds.size.y, bounds.min.z), -Vector3.up);
RaycastHit hit = new RaycastHit();
float meshHeightInverse = 1 / bounds.size.y;
Vector3 rayOrigin = ray.origin;
Vector2 stepXZ = new Vector2(bounds.size.x / resolution, bounds.size.z / resolution);
for (int zCount = 0; zCount < terrain.heightmapResolution; zCount++)
{
ShowProgressBar((zCount/ terrain.heightmapResolution), ChunkProgress);
for (int xCount = 0; xCount < terrain.heightmapResolution; xCount++)
{
float height = 0;
if (collider.Raycast(ray, out hit, bounds.size.y * 3))
{
height = (hit.point.y - bounds.min.y) * meshHeightInverse;
//clamp
if (height < 0)
{
height = 0;
}
}
heights[zCount, xCount] = height;
rayOrigin.x += stepXZ[0];
ray.origin = rayOrigin;
}
rayOrigin.z += stepXZ[1];
rayOrigin.x = bounds.min.x;
ray.origin = rayOrigin;
}
terrain.SetHeights(0, 0, heights);
//Create asset file
string fName = Address + oName + ".asset";
string[] searchin = new string[1];
searchin[0] = TerrainDataAddress.Remove(TerrainDataAddress.Length - 1);
//Debug.Log("looking in " + searchin[0]);
var existingFile = AssetDatabase.FindAssets(fName, searchin);
if (existingFile.Length > 0)
{
Debug.LogWarning("The file already exists @ " + existingFile[0]);
string existingaddress = AssetDatabase.GUIDToAssetPath(existingFile[0]);
AssetDatabase.DeleteAsset(existingaddress);
}
AssetDatabase.CreateAsset(terrain, fName);
EditorUtility.ClearProgressBar();
if (cleanUp != null)
{
cleanUp();
}
return terrainObject;
}
Your answer
Follow this Question
Related Questions
How can I add functions via a scriptswizzard variable? 0 Answers
Editing generic list through editorwindow 0 Answers
unity run editor function everytime I save a script 0 Answers
EditorObjExporter.cs script saves outside correct folder 0 Answers
Incremental terrain height increase limited by float size 1 Answer