144 lines
3.8 KiB
Python
144 lines
3.8 KiB
Python
|
# 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()
|