tornavis/tools/utils/make_shape_2d_from_blend.py

147 lines
3.8 KiB
Python

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