Posted by: Mohamed | 11/11/2013

Terrain deformation script.

Hello everyone,

Again, I’m not regularly blogging, but it’s really difficult to keep up with everything…
Now, since I promised sharing the script which deforms the terrain in real time, let’s dive into the subject 😉

Unfortunately the documentation of Unity3D is very poor when it comes to terrain deformation, and modifying the information of terrain’s height map.
I mean… Look at this: http://docs.unity3d.com/Documentation/ScriptReference/TerrainData.GetHeights.html
No examples, no details whatsoever, and what is more unfortunate also, even if you get the terrain deformation working, it’s still not fast, and if you use it as a core mechanic in your game, I think you have to adjust the resolution of your terrain to something manageable.

As you may already know, terrains in Unity3D are based on 2D height maps. These height maps are just gray-scale images with most black is the lowest point and most white is the highest peak in your terrain.
Unity is constantly monitoring the data from the height map file, and in order to modify the terrain in any way, you have to modify the height map, then Unity will reload the terrain automatically. You have to understand that any modification you do to the height map will be saved, and if you terminate your game and reload it again, whatever you’ve done to the terrain will be reloaded. In case you want your game to have untouched terrain every time the player starts a new game, then you have to save the height map info somewhere.

Let’s identify our problems:
1- We need to modify the terrain at a certain location
2- We need to map that location to our height map
3- We need to define the shape of the crater (or brush if you would like to call it that way 🙂 ) and use it to modify the terrain.

The following script is adjusted in a way that it can work with multiple terrains in your scene.
The code is fully commented. Further explanation will be provided after the code snippet if I saw that it’s needed… if something wasn’t clear for you, feel free to post your questions in the comments section, and I’ll gladly help ;).


using UnityEngine;
using System.Collections;

public class TerrainDeformer : MonoBehaviour
{

#region Public members
 //This class member is used for Crater texture shape, you have to make sure that the access of the texture is Read/Write. You can find it in the advanced section of the import option when you select the texture.
 public Texture2D CraterTexture; 
 // This class member modifies the intensity of the brush, if you don't introduce this factor, you will get harsh results, or you will not have a control over how hard your terrain is gonna be deformed
 public float AlphaIntensity;
 //Here you pass the terrain which you want to deform.
 public Terrain targetTerrain;
 #endregion


 #region Private members
 private float[,] SavedTerrainState { get; set; } // This class member is used to hold the original data of the terrain
 private float[,] craterShape { get; set; } //Since the height map of the terrain is 2D array, and since we are modifying square area of the terrain, we are extracting the gray-scale information from our crater texture, and keeping it to modify the terrain.
 private TextureInfo textureInfo { get; set; } // Texture info is a class I made in order to easily cache and manage the crater texture (brushes).
 #endregion

void Start () {
 // Remember, we are addressing a square map here, so you can imagine that 0,0 is the top left corner of the height map, and the terrain height map width and height are the maximum end points of the square
 SavedTerrainState = targetTerrain.terrainData.GetHeights(0, 0, targetTerrain.terrainData.heightmapWidth, targetTerrain.terrainData.heightmapHeight);
// Look at the constructor of the TextureInfo
textureInfo = new TextureInfo(CraterTexture, AlphaIntensity);
 }

void OnApplicationQuit()
 {
// Here I'm restoring the terrain to it's original state. SetHeights takes the starting point which 0 for x and 0 for y, and 2D array of gray-scale data defining the height map.
 targetTerrain.terrainData.SetHeights(0, 0, SavedTerrainState);
 }

void OnCollisionEnter(Collision _colObject)
 {
 // Here I'm deforming the terrain based on the position of the contact point. It could be a good idea to put a condition to make a certain object with a certain tag can modify the terrain, otherwise, anything which is colliding with the terrain will deform it.
  Utils.DeformTerrainOptimized(_colObject.contacts[0].point, targetTerrain, textureInfo);
 }
}

The script above is the main script which you gonna use/apply with any terrain you have in your scene. Now let’s move to the part where the magic is happening ;).


using UnityEngine;
using System.Collections;

public class Utils {
// This function/method, explains itself =D... basically it maps the point of collision on the height map of the terrain. Once we have the point on the height map, we can carry on from there.
public static Vector3 MapPositionOnHeightMap(Vector3 _v3Position, Terrain _terTerrain)
 {
 return new Vector3(((_v3Position.x - _terTerrain.transform.position.x) / _terTerrain.terrainData.size.x) * _terTerrain.terrainData.heightmapWidth, 0,
 ((_v3Position.z - _terTerrain.transform.position.z) / _terTerrain.terrainData.size.z) * _terTerrain.terrainData.heightmapHeight);
 }

<span style="line-height: 1.5;">/// <summary></span>

 /// Deform Terrain the optimized version
 /// </summary>
 /// <param name="_v3Position">Collision point</param>
 /// <param name="_terTerrain">Current terrain</param>
 /// <param name="TextureInfo">Deformation texture shape</param>
 public static void DeformTerrainOptimized(Vector3 _v3Position, Terrain _terTerrain, TextureInfo _texDeformation)
 {
 _v3Position = Utils.MapPositionOnHeightMap(_v3Position, _terTerrain); // Here we get the point on the height map.

// Right here we are getting the area which we need to modify. I'm dividing the texture's width and height by 2 just to get the center of the texture.. so basically the deformation will not start at the top left corner of the brush, but it will start right at the center of it.
var heightMap = _terTerrain.terrainData.GetHeights((int)_v3Position.x - (_texDeformation.Texture.width / 2), (int)_v3Position.z - (_texDeformation.Texture.height / 2), _texDeformation.Texture.width, _texDeformation.Texture.height); 
// This nested for loop is used to modify that area which we have've got using the information we got from our texture.
for (int i = 0; i < _texDeformation.Texture.height; i++)
 for (int j = 0; j < _texDeformation.Texture.width; j++)
 heightMap[i, j] -= _texDeformation.Alpha[i, j];
// Here we are resetting the height map of the terrain for that specific position. Note that we are not starting from 0,0 any more, but we are starting from the point of collision.
_terTerrain.terrainData.SetHeights((int)_v3Position.x - (_texDeformation.Texture.width / 2), (int)_v3Position.z - (_texDeformation.Texture.height / 2), heightMap);
 }
}

Moving on to the final class which is TextureInfo

using UnityEngine;
using System.Collections;

public class TextureInfo
{

#region Public members
 public Texture2D Texture { get; set; }
// I don't know why I just didn't say {private set;}, but I think it was quite late, and I'm quite lazy to change it now
public float[,] Alpha
 {
 get
 {
 return _Alpha;
 }
 }
 #endregion

#region Private members
 private float[,] _Alpha;
 #endregion
// Constructor with default alpha intensity, it could've been a smarter constructor with a default parameter, but again, it was late I think =D...
public TextureInfo(Texture2D _texTexture2D)
 {
 Texture = _texTexture2D;
 //unflatning 1D array into 2D array to feed it into the set heights. Get pixels returns all pixels in 1 dimensional array, and we need 2D array to modify our height map.
 var pixels = Texture.GetPixels();
 var craterShape = new float[Texture.width, Texture.height];

for (int i = 0; i < Texture.height; i++)
 for (int j = 0; j < Texture.width; j++)
 craterShape[i, j] = pixels[j * Texture.width + i].a * 0.03f;
 _Alpha = craterShape;
 }

public TextureInfo(Texture2D _texTexture2D, float _fAlphaIntensity)
 {
 Texture = _texTexture2D;
 //unflatning 1D array into 2D array to feed it into the set heights.
 var pixels = Texture.GetPixels();
 var craterShape = new float[Texture.width, Texture.height];

for (int i = 0; i < Texture.height; i++)
 for (int j = 0; j < Texture.width; j++)
 craterShape[i, j] = pixels[j * Texture.width + i].a * _fAlphaIntensity;
 _Alpha = craterShape;
 }

}

Remember that your texture has to have an Alpha channel. You could use a PSD file (which is the file format I’ve used in my case) or a PNG file ;).

Feel free to post  your comments or question in the comments, and I will gladly help if you needed any ;).
 

Posted by: Mohamed | 08/04/2013

Terrain deformation in Unity3D

Hello everyone,

It’s been a long time since my last update, but I’ve been very very busy lately…

Anyways, I would love to share with a terrain deformation project I made in Unity3D.
Terrain deformation in Unity3D is based on the idea that you have to modify the height map of the terrain you have in your scene, once you done, you can see whatever change you made in real time.

So here are the screenshots of the world I made and a picture of the terrain after deformation (left) (shall post a video later…)

Terraform 1 Terraform 2

In the next post I shall explain and post the scripts behind it, also I shall post a video showing all this in action ;).

Cheers 😉

Posted by: Mohamed | 17/03/2013

Final update on the 3D annotation tool ;)

Hello everyone,

This is my final post detailing the last update for the 3D annotation managment system that I’ve put together.

After I rewritten the tool for placing 3D annotations and rendering them, I’ve also rewritten the algorithm for managing the rendering of 3D annotations  Now there is the ability to filter the annotations up to n-sides. The user is able to input the number of sides (0 to n), and the annotation manager takes care of filtering and organizing the rendering based on user’s position with respect to the model. The same management system works on the iPad as well.

I would like to thank Pavel and Timo at Fraunhofer for their great help and support. it’s been awesome working at Fraunhofer IGD, and I hope that I will work with the team again =D.

Posted by: Mohamed | 07/03/2013

My first digital sculpture using Sculptris

Hello everyone,

I’ve just found in my archives a digital sculpture that I’ve started while having a Skype conversation with a friend using Sculptris. So in a period of 30min I’ve put the basic shape, then afterwards, I’ve spent about an hour and 30 min to 2 hours refining the model. Below you will find the screen shots of the model ;).

I’ve been working lately on improving the solution for floating 3D annotations.

Currently I’ve attached to each floating annotation a small rectangle where the text will fit in. The rectangle is automatically resized based on the text size. Also currently it is possible to filter the 3D annotations based on the camera position up to 8 separate areas (and it could be easily increased if needed). For example, if we have a huge object that a simple planar projection (planar means: showing annotations for the front and the back of the model based on user’s position with respect to the model) for displaying 3D annotations is not sufficient; we can make up to octagon projection of annotations  so the user will see floating annotations on one of the 8 sides of the 3D model, depending on the user’s position with respect to the 3D model.

Furthermore, another feature is added, which is automatically resize the annotations based on model’s size. The screenshot below is from the iPad while experimenting with one of the 3D models in our library.

Improving 3D annotations

Currently I’m rewriting/improving the tool for placing the annotation on the 3D objects.

Older Posts »

Categories