Search Unity

Blending two 3D meshes via script

Discussion in 'Scripting' started by skoteskote, Jan 8, 2018.

  1. skoteskote

    skoteskote

    Joined:
    Feb 15, 2017
    Posts:
    87
    Hi, I want to blend two different 3D objects, say a cat and a teapot, to create a sort of average between them. Much similar to how you would do it in 2D with the blend function in Illustrator (which ought to be easy to recreate in C#). The result will be wonky of course, but that's the point.

    I haven't written any code yet but asking here to see if anyone has seen any attempts to do something similar?

    Since the two 3D objects will have completely different point arrays I guess I need to go through each point on object A and find the closest point on object B, and this closest point would need to be in a specific angle relative to origin.
     
  2. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    In Illustrator, you would take two shapes and blend them together. The shapes have a few things in common. 1. they have a defined beginning and end and 2. they area all located on the same plane. So I can say.... this point on this mesh averages out to this point on this mesh because it is X percentage distance from start to end on the other mesh. Thus, giving you a clean defined path to create a morph.

    3d is not so lucky. You would have to do this by vertical striation across the horizontal striation. So, in the middle, you collect the shapes of both then do the same calculation, then for each striation, you have the same end result. You could create a voxel substructure to recreate your result, thus giving you a representation. This is incredibly complex.

    Now, in Illustrator.. you have 1 shape each.... no complex shapes, (no holes) You probably do not have this advantage in 3d, as holes are very common. This is another pain point.

    So, the conclusion that I could give you is that you should try using a voxel interface, converting both to voxels, They try to do some refactoring for those voxels to the new model. Once you are done, use a tetrahedron generator to create your new model.

    This means you should find some type of software that will try to do the same blend function using pixels instead of paths.
     
    skoteskote likes this.
  3. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,133
    skoteskote likes this.
  4. skoteskote

    skoteskote

    Joined:
    Feb 15, 2017
    Posts:
    87
    I managed to sort it out, here's the code if anyone need anything similar:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class BlendMesh : MonoBehaviour
    6. {
    7.  
    8.     public GameObject objectA;
    9.     public GameObject objectB;
    10.  
    11.     private Vector3[] newVertices;
    12.     private int[] newTriangles;
    13.  
    14.     private Mesh meshA;
    15.     private Mesh meshB;
    16.     private Mesh meshC;
    17.  
    18.     void Start ()
    19.     {
    20.    
    21.         meshA = objectA.GetComponent<MeshFilter> ().mesh;
    22.         meshB = objectB.GetComponent<MeshFilter> ().mesh;
    23.  
    24.         newVertices = meshA.vertices; //we'll overwrite these in the forloop
    25.         newTriangles = meshA.triangles;
    26.  
    27.         for (int i = 0; i < meshA.vertices.Length; i++) {
    28.  
    29.             Vector3 start = meshA.vertices[i];
    30.             Vector3 end = NearestVertexTo(start, meshB);;
    31.             newVertices[i] = ((end - start) * 0.5f) + start;
    32.        
    33.         }
    34.  
    35.         GameObject newObj = new GameObject(); //a new gameobject for creating the mesh
    36.         newObj.AddComponent<MeshRenderer>();
    37.         newObj.AddComponent<MeshFilter>();
    38.  
    39.         meshC = new Mesh();
    40.         newObj.GetComponent<MeshFilter>().mesh = meshC;
    41.         meshC.vertices = newVertices;
    42.         meshC.triangles = newTriangles;
    43.    
    44.     }
    45.  
    46.     void Update ()
    47.     {
    48.    
    49.     }
    50.  
    51.     public Vector3 NearestVertexTo(Vector3 point, Mesh mesh)
    52. {
    53.     // convert point to local space
    54.     point = transform.InverseTransformPoint(point);
    55.  
    56. float minDistanceSqr = Mathf.Infinity;
    57. Vector3 nearestVertex = Vector3.zero;
    58. // scan all vertices to find nearest
    59. foreach (Vector3 vertex in mesh.vertices)
    60. {
    61.      Vector3 diff = point-vertex;
    62.      float distSqr = diff.sqrMagnitude;
    63.      if (distSqr < minDistanceSqr)
    64.      {
    65.          minDistanceSqr = distSqr;
    66.          nearestVertex = vertex;
    67.      }
    68. }
    69. // convert nearest vertex back to world space
    70. return transform.TransformPoint(nearestVertex);
    71.  
    72. }
    73. }
    74.  
    It's not perfect but then who is. Since it uses the closest points on Object B relative to the vertices on Object A it has a bias towards Object A, you'd need to run it a few times on the resulting objects, swapping object A and B to get something that looks like a 50% blend.

    The resulting meshes also require quite a lot of cleaning up before being usable, many intersecting triangles.

    I used the NearestVertexTo from duck posted here https://answers.unity.com/questions/7788/closest-point-on-mesh-collider.html

    To then save the objects I first use this neat little tweak from Pharan to first save it as an asset: https://github.com/pharan/Unity-MeshSaver/blob/master/MeshSaver/Editor/MeshSaverEditor.cs

    I then use this to export it as a .obj http://wiki.unity3d.com/index.php?title=ObjExporter

    Here's a test with a Cat and a Deer
    https://imgur.com/LM7bzKE


     
    Last edited: Jan 12, 2018
    TomTrottel and bigmisterb like this.
  5. skoteskote

    skoteskote

    Joined:
    Feb 15, 2017
    Posts:
    87
    This is a really interesting way to do it, it would provide a lot more control over the final result. If I have time I will try it out! Maybe it's even possible to add some script to 3DCoat, which already uses voxels.
     
  6. Mohanadgad1

    Mohanadgad1

    Joined:
    Oct 9, 2020
    Posts:
    1
    how did you apply the forloop because it's gets me the same new shape
     
  7. skoteskote

    skoteskote

    Joined:
    Feb 15, 2017
    Posts:
    87
    How did you use the script? Did you have different meshes for objectA and B?