Search Unity

Question Any way to make a MeshCollider from vertices without using MeshCollider.sharedMesh?

Discussion in 'Physics' started by captainspaceman, Jan 20, 2021.

  1. captainspaceman

    captainspaceman

    Joined:
    Jul 14, 2017
    Posts:
    42
    I am making a voxel engine focusing on destruction physics. Basically, it works by generating a mesh from an array of "cubes." To destroy a cube, I am getting a raycast point and with a bit of math finding out what the index of that cube is in the cube data array based on the raycast point relative to the mesh (so it works with moving meshes and other offsets too), and then just completely redrawing the entire mesh from the cube data array after removing that one cube from it.

    This works just fine, except that every time I redraw the mesh, I use MeshCollider.sharedMesh = newMesh, which is extremely slow. When I loop through the whole chunk, I am already generating data that could tell me where the collider triangles should go (or however colliders work). Is there any way to draw a collider from vertices and triangles in the same way you can draw a mesh? Thank you!
     
  2. captainspaceman

    captainspaceman

    Joined:
    Jul 14, 2017
    Posts:
    42
    So I tried using quad collider prefabs and instantiating those as children to the chunk where the colliders should be and not using a mesh collider for the chunk at all. Once I started storing the collider objects in a list and only destroying or instantiating them when I need to, just changing the positions and rotations of ones that already exist, it is at least 4 times faster in the profiler than using MeshCollider.sharedMesh. I'm not saying my technique is fast but that using "MeshCollider.sharedMesh = newMesh" at runtime is very, very slow.

    This technique would work even for marching cubes, you'd just need to define all the possible collider shapes and spawn in the right ones. With just straight cubes, I was able to just use one quad prefab and change its offset and rotation as I went. I tested this and you can have literally millions of these objects in the scene and it hardly affects performance.

    Even when destroying hundreds of cubes in a matter of less than a second, I am above 30 fps. Mind you those cubes are being destroyed one by one and remaking their colliders for every single cube, it's not like an explosion where the mesh could be recalculated a single time, it is one object passing through another at high speed and destroying each cube as it goes.

    It is still slower than I would like, but it is acceptable and I think I may be able to think of ways to make it faster but anyway, if you have any suggestions I would love to hear them. I have already tried using an oversized buffer so I don't have to destroy or instantiate the colliders ever or use a list but it actually wasn't noticeably faster. My current system only destroys cubes or instantiates them when it needs to and deactivating and reactivating the objects had its own overhead. I'd also like to try greedy tiling but my bottleneck is with the physics and colliders so it probably wouldn't make a huge difference.
     
  3. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    I have no experience with this, but two things spring to mind...

    1) There are a lot of voxel / marching cube tutorials out there, do none of them address this problem?

    2) If they are cubes, would you not be better off keeping a list of the ones exposed to the surface and adding a box collider to them? Then you don't need to worry about which face is struck / needs rebuilding etc.
     
  4. captainspaceman

    captainspaceman

    Joined:
    Jul 14, 2017
    Posts:
    42
    @AITheSlacker I tried just that only using quad colliders and only creating the faces of the cubes I needed and with smaller chunks it was actually faster but testing it today with more practically sized chunks and MeshCollider.sharedMesh is about the same speed. My problem is that everything is perfectly fast enough for like a minecraft game, but my game will have very fast-moving objects, so I think I need to cast a ray out from each cube face(maybe vertices would be better), the length and direction of which is determined by that rigidbody's velocity, and upon destroying a single cube I get the velocity along with the position and use that to remove many cubes at once, rather than updating the mesh with every single destroyed cube. I just need to find a fast way of translating a direction into an operation based on a 3d grid which is actually a flat array.
     
  5. captainspaceman

    captainspaceman

    Joined:
    Jul 14, 2017
    Posts:
    42
    I figured out how to calculate the trajectory through the chunk! It's much faster now! So say we already found the index of the first cube we hit in the chunk's flat array, call it index c. What you do is you divide 1 by each axes of the normalized velocity of the object that made the raycast hit, round those three numbers to integers and then use them as counter limits. You make a loop, the length of which is the depth of the hole you are carving out of the chunk, you keep a counter for each axes and increment them every pass, if a counter is bigger than its axes counter limit, make the appropriate operation to c in that direction and reset the counter. So if our normalized velocity is (1, 0.5, 0), we know we need to increment c by the x axis every loop pass, and increment c by y every TWO loop passes. Destroying chunk[c] at the end of each loop pass and THEN recalculating the mesh.

    Basically, now instead of updating the mesh for every single destroyed cube, I am destroying a bunch of them at once and updating the mesh as minimally as possible.
     
    AlTheSlacker likes this.
  6. captainspaceman

    captainspaceman

    Joined:
    Jul 14, 2017
    Posts:
    42
    Man I got the trajectory thing working great but bringing the chunks and the world size closer to what I would want it at release and it drops to 15 fps for a moment when destroying a bunch of voxels. Which is unacceptable for multiplayer. And it is ALL meshCollider.sharedMesh = mesh. Or more specifically, the Mesh.Bake PhysX function I can see in the profiler. I can try using smaller chunks but the smaller my chunks the more likely it is that multiple chunks would need to be updated at once, so there must be a sweet spot but I can tell it would still hitch like this. I read a few other forum posts talking about this and none solved the problem. I tried meshCollider.sharedMesh.vertices = vertices to see if I could do the calculations manually, but it didn't update the collider, so what's the point of MeshCollider having a "SetVertices" method if it doesn't do anything? There should be a way to generate colliders manually, when using procedural meshes I already have all the data on the vertices and whatnot, I don't understand why I can't just tell it where to go... calculating the vertices, the triangles, recalculating normals, AND recalculating the UV altogether take less than 2ms, mostly recalculating normals, but baking the collider with meshCollider.sharedMesh takes about 20ms.
     
  7. captainspaceman

    captainspaceman

    Joined:
    Jul 14, 2017
    Posts:
    42
    Ok I know no one is going to read this but for anyone who is I tried it with a smaller chunk size and it is LIGHTING FAST! So if you have to use MeshCollider.sharedMesh = mesh at runtime and very often, make sure your meshes are simple and reduce the amount f times you update the mesh as much as possible. I just tried my system with 5x5x100 cube chunks and I can blast a hole through dozens of them at very high speeds and it doesn't even dip below 60 fps!
     
  8. captainspaceman

    captainspaceman

    Joined:
    Jul 14, 2017
    Posts:
    42
    I later learned the best way to do this is just to use the position data itself to detect collisions. That is to say, make a function that converts a Vector3 point into a voxel index in a chunk, then you can just use that chunk data to figure out if you hit a cube. This is actually the main advantage to using cubic voxels, collision detection is very simple, and you only need to check in 4 different directions around the player, and with moving voxels all you have to do is convert the position to a relative position of the moving chunk using movingChunk.InverseTransformPoint(point). In many different applications, voxels and octrees are used to represent positional data in a sort of lookup table of finitely-sized cubes. So cubic voxels are meant for collision detection. This takes the need for Unity's MeshCollider.sharedMesh function completely out of the equation, though it is a little more complicated.