6
\$\begingroup\$

Does Unity allow streaming mesh data that can be continuously changed?

I have a level that is dynamically changing based on some parameters. The number of triangles stays the same, they just change positions.

I'm speaking of around 2000 triangles that need their position updated each frame (they follow a spline that is computed on CPU), running on mobile phones.

I can't use a TrailRenderer because the object is a whole level. I can't use bones, because I need to know the final position for each vertex for my custom collision code.

The mesh should be marked Mesh.MarkDynamic.

\$\endgroup\$

2 Answers 2

10
\$\begingroup\$

Yes, you can change a mesh at runtime.

  1. get the current mesh from your object using Mesh mesh = GetComponent<MeshFilter>().mesh. Alternatively, if you want to replace the mesh with a completely new one, create one with Mesh mesh = new Mesh(); and assign it to your object with GetComponent<MeshFilter>().mesh = mesh;
  2. When you intend to modify the same mesh every frame, you should call the MarkDynamic() method on it. This might improve performance in that situation (thanks, Kroltan).
  3. Modify the Mesh object. It is a reference-type, so any changes you make will be reflected by the object which owns the mesh. For a minimum functional mesh you need to set at least its .vertices and .triangles component, if you want it to be textured also the uv component. If you are really just going to change the positions of vertices of an existing mesh, then the .vertices property is likely the only one you need. But keep in mind that you have little control over the order in which your 3d modeling program saves the vertices of a 3d model, so a slight change to the model can mean that the indexes of all vertices change in a completely unpredictable way.
  4. When you are finished, call mesh.RecalculateNormals() which is required by almost every shader to calculate brightness levels correctly.
  5. Calling mesh.RecalculateTangents() is also required for some shaders, especially those which use bump mapping. This step depends on the normals, so it needs to be executed after you recalculated the normals.
  6. You should also call mesh.RecalculateBounds(). This is required for reliably detecting if the mesh is off-screen (so it can be culled) and for collision detection if you are using it as a MeshCollider. This method actually gets called automatically when you set the .triangles, but not when you set the .vertices.

Will it be fast enough for mobile phones? That depends on countless factors, like what kind of mobile phone you are talking about, how often you are going to change the mesh, how effective your mesh changing algorithm is, what shaders you are using and much more. You need to test it for yourself. By the way, if your changes will usually be localized, it might help to break down your terrain into multiple sub-objects and only update the meshes of those which were affected.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ ah the most important point I think since I change it every frame. Thanks! \$\endgroup\$ Commented Jan 23, 2017 at 9:28
4
\$\begingroup\$

As Philipp said, you can change the mesh at runtime. You might have performance issues though (it's not "streaming" at all); in particular RecalculateNormals will probably be quite long.

If you're willing to accept the maintenance burden, another possibility is to compute the vertices position directly in the vertex shader. Since you have custom collision code, you'll have to compute 2 versions : one on the CPU, one on the GPU.

If you plan to use a MeshCollider, this idea is useless (it needs a Mesh anyway).

EDIT : I completely forgot about Mesh.MarkDynamic. This is definitely the way to go. I'll leave my answer just in case, though.

\$\endgroup\$
4
  • \$\begingroup\$ You could improve your answer by providing a simple example of a custom vertex shader in Unity. \$\endgroup\$
    – Philipp
    Commented Jan 22, 2017 at 16:26
  • \$\begingroup\$ It's all in the docs : docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html You just have to compute o.pos yourself. \$\endgroup\$
    – Calvin1602
    Commented Jan 22, 2017 at 17:06
  • \$\begingroup\$ it is not possible mesh "streaming" correct? for the normals I'll use some tricks to speed up since a /sqrt(magnitude) can be pretty expensive. Then mesh is changed everyframe by the way. \$\endgroup\$ Commented Jan 23, 2017 at 9:27
  • \$\begingroup\$ See my edit. Regarding sqrt() : you'll have to do it every frame anyway, so I'd rather do it on the GPU. \$\endgroup\$
    – Calvin1602
    Commented Jan 23, 2017 at 13:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .