File: make_shape_2d_from_blend.py

package info (click to toggle)
blender 4.3.2%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 309,564 kB
  • sloc: cpp: 2,385,210; python: 330,236; ansic: 280,972; xml: 2,446; sh: 972; javascript: 317; makefile: 170
file content (146 lines) | stat: -rw-r--r-- 3,891 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# SPDX-FileCopyrightText: 2023 Blender Authors
#
# SPDX-License-Identifier: Apache-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)

    bm.normal_update()

    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()