Skip to content
Snippets Groups Projects
make_shape_2d_from_blend.py 3.74 KiB
Newer Older
  • Learn to ignore specific revisions
  • # Apache License, Version 2.0
    '''
    Created compact byte arrays which can be decoded into 2D shapes.
    (See 'GPU_batch_from_poly_2d_encoded').
    
    - Objects must use the prefix "shape_"
    - Meshes and Curves are supported as input.
    - C and Python output is written to "output/"
    
    The format is simple: a series of  (X, Y) locations one byte each.
    Repeating the same value terminates the polygon, moving onto the next.
    
    Example Use::
    
       blender.bin -b --factory-startup my_shapes.blend --python make_shape_2d_from_blend.py
    '''
    import bpy
    import os
    
    USE_C_STYLE = True
    USE_PY_STYLE = True
    
    WRAP_LIMIT = 79
    TAB_WIDTH = 4
    
    SUBDIR = "output"
    
    
    def float_to_ubyte(f):
        return max(0, min(255, int(round(f * 255.0))))
    
    
    def curve_to_loops(ob):
        import bmesh
        cu = ob.data
    
    
        me = ob.to_mesh()
    
        bm = bmesh.new()
        bm.from_mesh(me)
    
        me = ob.to_mesh_clear()
    
    
        bmesh.ops.beautify_fill(bm, faces=bm.faces, edges=bm.edges)
    
        edges = bm.edges[:]
        edges.sort(key=lambda e: e.calc_length(), reverse=True)
    
        for e in edges:
            if e.is_manifold:
                f_a, f_b = [f for f in e.link_faces]
                bmesh.utils.face_join((f_a, f_b), False)
    
        edges = bm.edges[:]
        for e in edges:
            if e.is_wire:
                bm.edges.remove(e)
    
    
        data_all = []
        for f in bm.faces:
            points = []
    
            # Ensure all faces are pointing the correct direction
            # Note, we may want to use polygon sign for a second color
            # (via the material index).
            if f.normal.z > 0.0:
                loops = f.loops
            else:
                loops = reversed(f.loops)
            for l in loops:
    
                points.append(
                    tuple(float_to_ubyte(axis) for axis in l.vert.co.xy)
                )
            data_all.append((points, f.material_index))
        bm.free()
        return data_all
    
    
    def write_c(ob):
        cu = ob.data
        name = ob.name
        with open(os.path.join(SUBDIR, name + ".c"), 'w') as fh:
            fw = fh.write
            fw(f"/* {name} */\n")
            fw(f"const uchar {name}[] = {{")
            line_len = WRAP_LIMIT
            line_is_first = True
            array_len = 0
            data_all = curve_to_loops(ob)
            for (points, material_index) in data_all:
                # TODO, material_index
                for p in points + [points[-1]]:
                    line_len += 12
                    if line_len >= WRAP_LIMIT:
                        fw("\n\t")
                        line_len = TAB_WIDTH
                        line_is_first = True
                    if not line_is_first:
                        fw(" ")
                    fw(", ".join([f"0x{axis:02x}" for axis in p]) + ",")
                    line_is_first = False
                array_len += (len(points) + 1) * 2
            fw("\n};\n")
            # fw(f"const int data_len = {array_len}\n")
    
    
    def write_py(ob):
        cu = ob.data
        name = ob.name
        with open(os.path.join(SUBDIR, name + ".py"), 'w') as fh:
            fw = fh.write
            fw(f"# {name}\n")
            fw("data = (")
            line_len = WRAP_LIMIT
            fw = fh.write
            data_all = curve_to_loops(ob)
            for (points, material_index) in data_all:
                # TODO, material_index
    
                for p in points + [points[-1]]:
                    line_len += 8
    
                    if line_len >= WRAP_LIMIT:
    
                        if p is not points[0]:
                            fw("'")
                        fw("\n    b'")
    
                        line_len = 6
    
                    fw("".join([f"\\x{axis:02x}" for axis in p]))
    
            fw("'\n)\n")
    
    
    def main():
        os.makedirs(SUBDIR, exist_ok=True)
        for ob in bpy.data.objects:
            if ob.type not in {'MESH', 'CURVE'}:
                continue
            if not ob.name.startswith('shape_'):
                continue
            if USE_C_STYLE:
                write_c(ob)
            if USE_PY_STYLE:
                write_py(ob)
    
    
    if __name__ == "__main__":
        main()