skoch9 / meshplot

Plot 3D triangle meshes
GNU General Public License v3.0
145 stars 37 forks source link

Adding transparency/opacity/alpha #30

Open ljmartin opened 3 years ago

ljmartin commented 3 years ago

Hi meshplot, Have really benefited a lot from this library so thank you. I'd like to visualise a mesh that is partially transparent. Is this option available already?

Happy to contribute something but I'm not sure where to start - at a complete guess I figure https://github.com/skoch9/meshplot/blob/d780cc5db68d545fb68e1f81176ce63818712682/meshplot/Viewer.py#L230 which (I think) instantiates the mesh from pythreejs. Looks like the pythreejs MeshStandardMaterial does have an option alphaMap to handle transparency.

Thanks! Lewis

jiangzhongshi commented 3 years ago

Something like this was working for me

def add_transparent_mesh(self, v, f, c=None, uv=None, n=None, shading={}, opacity=0.6):
    import pythreejs as p3s
    sh = self._Viewer__get_shading(shading)
    mesh_obj = {}

    #it is a tet
    if v.shape[1] == 3 and f.shape[1] == 4:
        f_tmp = np.ndarray([f.shape[0]*4, 3], dtype=f.dtype)
        for i in range(f.shape[0]):
            f_tmp[i*4+0] = np.array([f[i][1], f[i][0], f[i][2]])
            f_tmp[i*4+1] = np.array([f[i][0], f[i][1], f[i][3]])
            f_tmp[i*4+2] = np.array([f[i][1], f[i][2], f[i][3]])
            f_tmp[i*4+3] = np.array([f[i][2], f[i][0], f[i][3]])
        f = f_tmp

    if v.shape[1] == 2:
        v = np.append(v, np.zeros([v.shape[0], 1]), 1)

    # Type adjustment vertices
    v = v.astype("float32", copy=False)

    # Color setup
    colors, coloring = self._Viewer__get_colors(v, f, c, sh)

    # Type adjustment faces and colors
    c = colors.astype("float32", copy=False)

    # Material and geometry setup
    ba_dict = {"color": p3s.BufferAttribute(c)}
    if coloring == "FaceColors":
        verts = np.zeros((f.shape[0]*3, 3), dtype="float32")
        for ii in range(f.shape[0]):
            #print(ii*3, f[ii])
            verts[ii*3] = v[f[ii,0]]
            verts[ii*3+1] = v[f[ii,1]]
            verts[ii*3+2] = v[f[ii,2]]
        v = verts
    else:
        f = f.astype("uint32", copy=False).ravel()
        ba_dict["index"] = p3s.BufferAttribute(f, normalized=False)

    ba_dict["position"] = p3s.BufferAttribute(v, normalized=False)

    if uv is not None:
        uv = (uv - np.min(uv)) / (np.max(uv) - np.min(uv))
        # tex = p3s.DataTexture(data=texture_data, format="RGBFormat", type="FloatType")
        material = p3s.MeshStandardMaterial(map=texture_data, reflectivity=sh["reflectivity"], side=sh["side"],
                roughness=sh["roughness"], metalness=sh["metalness"], flatShading=sh["flat"],
                polygonOffset=True, polygonOffsetFactor= 1, polygonOffsetUnits=5)
        ba_dict["uv"] = p3s.BufferAttribute(uv.astype("float32", copy=False))
    else:
        material = p3s.MeshStandardMaterial(vertexColors=coloring, reflectivity=sh["reflectivity"],
                    side=sh["side"], roughness=sh["roughness"], metalness=sh["metalness"], 
                                            opacity=opacity, transparent=True,alphaTest=opacity*0.99,
                                            blending='CustomBlending',depthWrite=False,
                    flatShading=True)

    if type(n) != type(None) and coloring == "VertexColors":
        ba_dict["normal"] = p3s.BufferAttribute(n.astype("float32", copy=False), normalized=True)

    geometry = p3s.BufferGeometry(attributes=ba_dict)

    if coloring == "VertexColors" and type(n) == type(None):
        geometry.exec_three_obj_method('computeVertexNormals')
    elif coloring == "FaceColors" and type(n) == type(None):
        geometry.exec_three_obj_method('computeFaceNormals')

    # Mesh setup
    mesh = p3s.Mesh(geometry=geometry, material=material)

    # Wireframe setup
    mesh_obj["wireframe"] = None

    # Object setup
    mesh_obj["max"] = np.max(v, axis=0)
    mesh_obj["min"] = np.min(v, axis=0)
    mesh_obj["geometry"] = geometry
    mesh_obj["mesh"] = mesh
    mesh_obj["material"] = material
    mesh_obj["type"] = "Mesh"
    mesh_obj["shading"] = sh
    mesh_obj["coloring"] = coloring

    return self._Viewer__add_object(mesh_obj)

Would be used as

plt = mp.Viewer(dict())
add_transparent_mesh(plt, v,f)
ljmartin commented 3 years ago

love it, thanks! What is the purpose of f_tmp? Isn't that just copying f and then resetting f, (as itself)? Or is it reordering the winding.

jiangzhongshi commented 3 years ago

Hi,

That part I took from the original code (not really relevant to the transparent part I edited), and the purpose was: assuming f is a tet mesh, split each to 4 triangles for visualize.