Skip to content

Instantly share code, notes, and snippets.

@alexmalyutindev
Last active November 21, 2024 14:38
Show Gist options
  • Save alexmalyutindev/8438b1e10d3817a1d5e6c20e35720d39 to your computer and use it in GitHub Desktop.
Save alexmalyutindev/8438b1e10d3817a1d5e6c20e35720d39 to your computer and use it in GitHub Desktop.

Revisions

  1. alexmalyutindev revised this gist Oct 24, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion bake_morph_textures.py
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@
    def bake_morph_textures(obj, frame_range, scale, name, output_dir):
    """ Bake and export morph textures for the specified object and frame range
    obj -- the object to export (frame 0 is default pose)
    frame range -- List [start frame, end frame]
    frame_range -- List [start frame, end frame]
    scale -- fL. maximum vertex position along any axis for rescaling positions to 0..1
    name -- str. prefix for output files
    outup_dir -- str. output directory for mesh + textures
  2. alexmalyutindev revised this gist Oct 24, 2023. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions bake_morph_textures.py
    Original file line number Diff line number Diff line change
    @@ -90,6 +90,7 @@ def get_vertex_data_from_frame(obj, position_scale):

    return vertex_data


    def unsign_vector(vec, as_list=True):
    """Rescale input vector from -1..1 to 0..1.
    """
    @@ -101,6 +102,7 @@ def unsign_vector(vec, as_list=True):
    else:
    return vec


    def create_morph_uv_set(obj):

    bm = bmesh.new()
    @@ -122,6 +124,7 @@ def create_morph_uv_set(obj):
    exportobj = bm
    bm.to_mesh(obj.data)


    def export_mesh(obj, output_dir, name):
    context = bpy.context
    context.collection.objects.link(obj)
  3. alexmalyutindev created this gist Oct 24, 2023.
    136 changes: 136 additions & 0 deletions bake_morph_textures.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,136 @@
    # Source
    # Vertex animation textures, beanbags and boneless animations
    # by Martin Donald
    # https://www.youtube.com/watch?v=NQ5Dllbxbz4

    import bpy
    import bmesh
    import mathutils


    def bake_morph_textures(obj, frame_range, scale, name, output_dir):
    """ Bake and export morph textures for the specified object and frame range
    obj -- the object to export (frame 0 is default pose)
    frame range -- List [start frame, end frame]
    scale -- fL. maximum vertex position along any axis for rescaling positions to 0..1
    name -- str. prefix for output files
    outup_dir -- str. output directory for mesh + textures
    """

    scene_path = bpy.path.abspath("//")
    if "//" in output_dir:
    output_dir = output_dir.replace("//", scene_path)

    pixels_pos = list()
    pixels_nrm = list()
    pixels_tng = list()
    width = 0
    for i in range(frame_range[1] - frame_range[0]):
    f = i + frame_range[0]
    temp_obj = new_object_from_frame(obj, f)
    new_pixels = get_vertex_data_from_frame(temp_obj, scale)
    width = len(new_pixels)
    for pixel in new_pixels:
    pixels_pos += pixel[0]
    pixels_nrm += pixel[1]
    pixels_tng += pixel[2]

    height = frame_range[1] - frame_range[0]
    write_output_image(pixels_pos, name + '_position', [width, height], output_dir)
    write_output_image(pixels_nrm, name + '_normal', [width, height], output_dir)
    write_output_image(pixels_tng, name + '_tangent', [width, height], output_dir)

    frame_zero = new_object_from_frame(obj, 0)
    create_morph_uv_set(frame_zero)
    export_mesh(frame_zero, output_dir, name)

    return frame_zero


    def write_output_image(pixel_list, name, size, output_dir):
    image = bpy. data. images.new(name, width=size[0], height=size[1])
    image.pixels = pixel_list
    image. save_render(output_dir + name + ".png", scene=bpy. context. scene)


    def new_object_from_frame(obj, f):
    """ Create a new mesh from the evaluated version of obj at frame f.
    """

    context = bpy.context
    scene = context.scene
    scene.frame_set(f) # the correct way to set a frame and update depsgraph
    # need depsgraph to access evaluated mesh attributes
    dg = context.view_layer.depsgraph
    eval_obj = obj.evaluated_get(dg)
    duplicate = bpy.data.objects.new('frame_0', bpy.data.meshes.new_from_object(eval_obj))
    return duplicate


    def get_vertex_data_from_frame(obj, position_scale):
    """ Given an object, return the Position, Normal and Tangent for each vertex.
    """

    obj.data.calc_tangents()
    vertex_data = [None] * len (obj.data.vertices)

    for face in obj.data.polygons:
    for vert in [obj.data.loops[i] for i in face.loop_indices]:
    index = vert.vertex_index
    tangent = unsign_vector(vert.tangent.copy())
    normal = unsign_vector(vert.normal.copy())
    position = unsign_vector(obj.data.vertices[index].co.copy() / position_scale)

    # Image object expects RGBA, append A
    tangent.append(1.0)
    normal.append(1.0)
    position.append(1.0)

    vertex_data[index] = [position, normal, tangent]

    return vertex_data

    def unsign_vector(vec, as_list=True):
    """Rescale input vector from -1..1 to 0..1.
    """

    vec += mathutils. Vector((1.0, 1.0, 1.0))
    vec /= 2.0
    if as_list:
    return list(vec.to_tuple())
    else:
    return vec

    def create_morph_uv_set(obj):

    bm = bmesh.new()
    bm.from_mesh(obj.data)

    #not sure why i make two but thats also what houdini does
    uv_layer = bm.loops.layers.uv.new("uv")
    uv_layer2 = bm.loops.layers.uv.new("uv2")

    pixel_size = 1.0 / len(bm.verts)

    i = 0
    for v in bm.verts:
    for l in v.link_loops:
    uv_data = l[uv_layer]
    uv_data.uv = mathutils.Vector((i * pixel_size, 0.0))
    i += 1

    exportobj = bm
    bm.to_mesh(obj.data)

    def export_mesh(obj, output_dir, name):
    context = bpy.context
    context.collection.objects.link(obj)
    context.view_layer.objects.active = obj

    #this is super ugly but works
    bpy.data.objects['export'].select_set(True)
    bpy.data.objects['Plane'].select_set(False)

    output_dir = output_dir + name + "_" + "mesh.fbx"

    bpy.ops.export_scene.fbx(use_selection=True, filepath=output_dir)