- Home /
Edit Terrain foliage/texture at runtime
Hi guys, please excuse me, I've only been using the Unity engine for a few days. However, I really love what I've seen so far and I'm keen to continue using it.
Basically, I want to be able to edit the grass layer at runtime. Specifically, when my vehicle drives over the grass, the grass will be removed. Now, I did some research already and came across a few articles and topics, and from what I can gather there are some undocumented methods relating to editing the terrain layers at runtime. But, I can't see how the code relates, that's where you come in.
I'd love for someone to show/explain to me the following points (JavaScript would be preferable):
- How to reference the terrain layer that I want to edit (the particular foliage layer or texture layer)
- How to manipulate that layer to remove the grass/texture at the vehicle's current position
- How to reapply the texture layer that I have edited.
Even if you can only help me a little bit, all help will be appreciated.
Best regards, Russ
See also: Can I modify grass or details on the terrain at runtime?
Answer by duck · Mar 26, 2010 at 10:00 AM
First of all, do you actually want to modify the details or the textures (or both)?
The terrain textures are the ones which are mapped onto the surface of the terrain like paint. The details are either transparent textures which are placed perpendicular to the terrain surface (like a flat picture of some grass standing upright), or they can be actual small 3d models scattered around the surface.
The broad technique for modifying both is similar, however the data for each of these types is stored differently.
Basically, the data that controls the mixing of the terrain textures, and the data which controls the concentration of 'details' (i.e. your small plants, grass, bushes, etc) are each stored in a 2d grid formation. There's one grid for each, and each grid has a certain resolution.
These are what the "Detail Resolution" and the "Control Texture Resolution" sizes are for in the Create Terrain dialogue box.
Whatever the resolution you choose for each grid, the data contained is mapped across the entire width and length of your terrain, much like a texture mapped onto a plane.
The data contained within each grid however, is different depending on whether you're looking at the Detail data, or the Control Texture (the data which controls the mixing of your terrain textures).
Detail data.
For the Detail data, each grid cell represents a certain area of the terrain. The lower the resolution of your detail grid, the larger the area is that is controlled by each grid value. The value in each 'grid cell' defines the concentration of detail textures/models displayed within that cell. For this reason, the detail grid contains whole numbers (integers) which literally represent the count of detail items within the area that the cell covers.
If you have more than one type of detail texture, each cell has a corresponding 'layer', representing the count of that type of detail texture, in that cell. Therefore, the whole detail 'grid' is actually a 3D Array, with the 3 dimensions representing:
- X: width across the terrain
- Y: length along the terrain
- Layer: the index number of each type of detail
Unity's (undocumented) SetDetailLayer and GetDetailLayer commands - which you found in one of my other answers - give you access to read and write this data, so to "cut the grass" with regard to the detail data, it would be a case of calculating which cell the mower currently occupies, and setting the detail count to zero for one or all of the layers in that cell.
Surface Texture Data
For the surface texture data, again, each grid cell represents an area of your terrain, and the lower the resolution, the larger the area that each cell value represents. The data in each grid cell here represents the various strengths at which your textures are mixed together at that particular spot on the terrain. This data is also stored in a grid which has a 3rd dimension. In this instance, each 'layer' of the grid's 3rd dimension represents each of your surface textures, and the value contained within each cell layer is a floating point value between zero and one, so the 3 dimensions represent:
- X: width across the terrain
- Y: length along the terrain
- Layer: the index number of each surface texture
(and the sum of all layers for any given X,Y should add up to 1)
Each of these values represents the mixing strength of each texture, and the textures are all mixed with each other, there's an extra complication here - which is that the sum of all the layers for any given cell should add up to one.
The (again, undocumented) functions in Unity which give you access to get and set this data refers to the grid as the alpha map, and the specific use of them can be found in another of my answers, here.
So, to "cut grass" with regard to the terrain textures (the alpha map) data, it would be a case of calculating which cell the mower currently occupies, and setting the blending weight value of the grass layer to zero, and correspondingly adjusting the weights of the other layers to add up to 1. If you have only two textures (grassy, and 'mown'), this would be simply a case of setting the grass weight to 0.0 and the 'mown' texture weight to 1.0.
Now, both the detail functions and the alphamap functions mentioned above allow you to get and set specific areas of the whole grid. You'll want to make use of this to only get and set from the specific grid cell that your mower is in, so the final piece of the puzzle for you would be: How do I determine which grid cell my mower is in, so that I can change the values for the detail/alphamap in the correct cell?
Lucky for you, I also answered this in another terrain-related question: How to translate world coordinates to terrain coordinates?. In that particular question, it's being used to get the correct grid cell for the heightmap, but the technique would be exactly the same for finding the correct location in the detail grid, or the alpha map grid.
So, now you should have all the pieces of the puzzle that you need to put together your project. Hope this has shed some light on how it works. It's a complex area, and your project certain isn't trivial - and having most of the terrain system functions undocumented only makes it harder!
Standard disclaimer: Some of these functions are undocumented. Any future updates of the unity engine might change or remove these functions from the API. This means your project may not work in future versions of the Unity editor, and webplayer builds may not work with future versions of the plugin.
Thanks for your excellent answer, and explaining it further. I will now try to write something which works, and get back to you with my result.
Thanks again!
Hi, i had a little go but i can't seem to get it working. However, i'm so close i can almost smell the cut grass :)
Right now im trying to edit the texture so one of the layers is painted on..
http://pastebin.com/X0CT5$$anonymous$$PZ
Can you give me any pointers? i think i'm nearly there with it.
You're on the right track, but you need to use GetAlpha$$anonymous$$aps to get data from the existing alphamap ins$$anonymous$$d of creating a new blank alpha map texture every frame. Also, you need only get and set data at the area that you want to edit (not the full size of the alphamap). That's why the "get" command has offset and width & height, and the "set" command has an offset.
Okay. I tried "float[, ,] splatmapData = terrainData.GetAlpha$$anonymous$$aps" ins$$anonymous$$d of "float[, ,] splatmapData = new float....." but unity says that GetAlpha$$anonymous$$aps doesn't exist? Also, You lost me a bit about only getting and setting the data I need. So i would specify GetAlpha$$anonymous$$aps with the position of the data i need to get?
Scrap that, terrainData.GetAlpha$$anonymous$$aps is working. I still can't see what's going wrong though.. http://pastebin.com/cp8Vc3Dk
Answer by e-bonneville · Mar 25, 2010 at 08:54 PM
Well, you wouldn't want to edit the terrain directly. Best approach would be to lay down a prefab of a flat track picture coming from your wheels. Try this code (untested Javascript):
var wheel : Transform;
function OnCollisionEnter(collision : Collision) { // Rotate the object so that the y-axis faces along the normal of the surface var contact = collision.contacts[0]; var rot = Quaternion.FromToRotation(Vector3.up, contact.normal); var pos = contact.point; Instantiate(tracks, pos, rot); }
Stick that on the wheel. Problem with this is that if your wheel hits a wall, it'll put tracks on the wall... Unless that's what you want! Good luck;
Elliot Bonneville, A.K.A. elbon96
I know it sounds bizarre but I want something which can be saved you see. It's for a simulator which involves "grass cutting". But thanks for your help, no doubt your suggestion will come in handy in the future.
yeah, i accidentally posted by answer before it was finished. woops!
Oooh, i see. not like car tracks or anything, right...
Yeah, in that case, i wouldn't be of any help to you. :(
No worries mate, I will still be able to use your theory for other parts of the simulation :) But yeah my requirements are quite specific. I've seen the code elsewhere on here but I just need some help understanding how it works.
Answer by runevision · Mar 26, 2010 at 09:28 AM
See this question:
He already said that he's found that page in the comments to elbon96's answer.
Your answer
Follow this Question
Related Questions
Dense foliage on Android seen from above, performance, can it be done? 1 Answer
Multiple terrains and how LoD works 0 Answers
Grass get darker problem 0 Answers
Procedural grass placement 0 Answers
Small seams on png files 3 Answers