From Unify Community Wiki
Revision as of 01:37, 22 December 2009 by Eric5h5 (Talk | contribs)

Jump to: navigation, search

Author: Eric Haines (Eric5h5)


Converts an object mesh to a heightmap. This way you can create terrain meshes in a standard 3D app such as Blender or Maya and convert it to a Unity terrain. (See also TerrainObjExporter, which saves a Unity terrain as an .obj file.)


You must place the script in a folder named Editor in your project's Assets folder for it to work properly.

Click on an object in the scene view or hierarchy, then select Object to Terrain from the Terrain menu. The object is then converted to the heightmap in the active terrain. The object you're clicking on must contain the mesh itself--for example, selecting a root transform object, where the actual mesh is contained in the child object, won't work. If in doubt, simply open the mesh asset in the project view, then drag the bare mesh into the scene or hierarchy:


This function uses the axis-aligned bounding box of the mesh, so object rotations on the x and z axis other than 0, or rotations on the y axis other than multiples of 90 degrees, may give somewhat odd results.

You will probably need to adjust the height of the terrain to match the object mesh (using Set Resolution… in the Terrain menu).

JavaScript - Object2Terrain.js

<javascript> @MenuItem ("Terrain/Object to Terrain")

static function Object2Terrain () { // See if a valid object is selected var obj = Selection.activeObject as GameObject; if (obj == null) { EditorUtility.DisplayDialog("No object selected", "Please select an object.", "Cancel"); return; } if (obj.GetComponent(MeshFilter) == null) { EditorUtility.DisplayDialog("No mesh selected", "Please select an object with a mesh.", "Cancel"); return; } else if ((obj.GetComponent(MeshFilter) as MeshFilter).sharedMesh == null) { EditorUtility.DisplayDialog("No mesh selected", "Please select an object with a valid mesh.", "Cancel"); return; } if (Terrain.activeTerrain == null) { EditorUtility.DisplayDialog("No terrain found", "Please make sure a terrain exists.", "Cancel"); return; } var terrain = Terrain.activeTerrain.terrainData;

// If there's no mesh collider, add one (and then remove it later when done) var addedCollider = false; var addedMesh = false; var objCollider = obj.collider as MeshCollider; if (objCollider == null) { objCollider = obj.AddComponent(MeshCollider); addedCollider = true; } else if (objCollider.sharedMesh == null) { objCollider.sharedMesh = (obj.GetComponent(MeshFilter) as MeshFilter).sharedMesh; addedMesh = true; }

Undo.RegisterUndo (terrain, "Object to Terrain");

var resolutionX = terrain.heightmapWidth; var resolutionZ = terrain.heightmapHeight; var heights = terrain.GetHeights(0, 0, resolutionX, resolutionZ);

// Use bounds a bit smaller than the actual object; otherwise raycasting tends to miss at the edges var objectBounds = objCollider.bounds; var leftEdge = objectBounds.center.x - objectBounds.extents.x + .01; var bottomEdge = objectBounds.center.z - objectBounds.extents.z + .01; var stepX = (objectBounds.size.x - .019) / resolutionX; var stepZ = (objectBounds.size.z - .019) / resolutionZ;

// Set up raycast vars var y = objectBounds.center.y + objectBounds.extents.y + .01; var hit : RaycastHit; var ray = new Ray(Vector3.zero, -Vector3.up); var rayDistance = objectBounds.size.y + .02; var heightFactor = 1.0 / rayDistance;

// Do raycasting samples over the object to see what terrain heights should be var z = bottomEdge; for (zCount = 0; zCount < resolutionZ; zCount++) { var x = leftEdge; for (xCount = 0; xCount < resolutionX; xCount++) { ray.origin = Vector3(x, y, z); if (objCollider.Raycast(ray, hit, rayDistance)) { heights[zCount, xCount] = 1.0 - (y - hit.point.y)*heightFactor; } else { heights[zCount, xCount] = 0.0; } x += stepX; } z += stepZ; }

terrain.SetHeights(0, 0, heights);

if (addedMesh) { objCollider.sharedMesh = null; } if (addedCollider) { DestroyImmediate(objCollider); } } </javascript>

Personal tools