Update generate_book.py
Browse files- generate_book.py +15 -15
generate_book.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import bpy, bmesh, json, sys, os, math
|
| 2 |
|
| 3 |
-
#
|
| 4 |
argv = sys.argv
|
| 5 |
argv = argv[argv.index("--")+1:] if "--" in argv else []
|
| 6 |
args = { argv[i].lstrip("-"): argv[i+1] for i in range(0, len(argv), 2) }
|
|
@@ -9,43 +9,43 @@ spec_path = args.get("spec"); out_dir = args.get("out", os.getcwd())
|
|
| 9 |
with open(spec_path, "r") as f:
|
| 10 |
spec = json.load(f)
|
| 11 |
|
| 12 |
-
name = spec.get("name","Book")
|
| 13 |
sx, sy, sz = (spec["size_m"]["x"], spec["size_m"]["y"], spec["size_m"]["z"])
|
| 14 |
-
bevel = spec.get("bevel_m", 0.002)
|
| 15 |
-
corner_caps = spec.get("corner_caps", True)
|
| 16 |
strap = spec.get("strap", {"enabled": False})
|
| 17 |
|
| 18 |
-
#
|
| 19 |
bpy.ops.wm.read_factory_settings(use_empty=True)
|
| 20 |
|
| 21 |
-
#
|
| 22 |
mesh = bpy.data.meshes.new("book_lp")
|
| 23 |
obj = bpy.data.objects.new(name, mesh)
|
| 24 |
bpy.context.scene.collection.objects.link(obj)
|
| 25 |
bm = bmesh.new()
|
| 26 |
-
bmesh.ops.create_cube(bm, size=1.0)
|
| 27 |
bm.to_mesh(mesh); bm.free()
|
| 28 |
|
| 29 |
-
# scale to meters (cube size=
|
| 30 |
obj.scale = (sx/2, sy/2, sz/2)
|
| 31 |
bpy.context.view_layer.objects.active = obj
|
| 32 |
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
| 33 |
|
| 34 |
-
# bevel
|
| 35 |
bev = obj.modifiers.new("Bevel","BEVEL")
|
| 36 |
bev.width = bevel
|
| 37 |
bev.segments = 2
|
| 38 |
bev.limit_method = 'ANGLE'
|
| 39 |
bev.angle_limit = math.radians(60)
|
| 40 |
|
| 41 |
-
# simple spine loopcut (visual separation
|
| 42 |
bpy.ops.object.mode_set(mode='EDIT')
|
| 43 |
bpy.ops.mesh.loopcut_slide(MESH_OT_loopcut={"number_cuts":1}, TRANSFORM_OT_edge_slide={"value":0.0})
|
| 44 |
bpy.ops.object.mode_set(mode='OBJECT')
|
| 45 |
|
| 46 |
# optional strap
|
| 47 |
if strap.get("enabled", False):
|
| 48 |
-
w = strap.get("width_m", 0.025)
|
| 49 |
bpy.ops.mesh.primitive_cube_add(size=1)
|
| 50 |
s = bpy.context.active_object; s.name = "strap"
|
| 51 |
s.scale = (sx*0.55, w/2, sz/2*1.05)
|
|
@@ -80,19 +80,19 @@ bpy.ops.object.modifier_apply(modifier=bev.name)
|
|
| 80 |
obj.data.use_auto_smooth = True
|
| 81 |
bpy.ops.object.shade_smooth()
|
| 82 |
|
| 83 |
-
#
|
| 84 |
bpy.ops.object.mode_set(mode='EDIT')
|
| 85 |
bpy.ops.uv.smart_project(angle_limit=66, island_margin=0.02)
|
| 86 |
bpy.ops.object.mode_set(mode='OBJECT')
|
| 87 |
|
| 88 |
-
# simple PBR material
|
| 89 |
mat = bpy.data.materials.new("MAT_Book")
|
| 90 |
mat.use_nodes = True
|
| 91 |
nt = mat.node_tree
|
| 92 |
bsdf = nt.nodes.get("Principled BSDF")
|
| 93 |
base = nt.nodes.new("ShaderNodeRGB")
|
| 94 |
-
leather = spec.get("style", {}).get("leather", "dark_brown")
|
| 95 |
-
base.outputs[0].default_value = (0.12,0.06,0.03,1.0) if "brown" in leather else (0.2,0.2,0.2,1.0)
|
| 96 |
nt.links.new(base.outputs["Color"], bsdf.inputs["Base Color"])
|
| 97 |
bsdf.inputs["Roughness"].default_value = 0.6
|
| 98 |
bsdf.inputs["Metallic"].default_value = 0.0
|
|
|
|
| 1 |
import bpy, bmesh, json, sys, os, math
|
| 2 |
|
| 3 |
+
# --- parse args: blender -b -P generate_book.py -- --spec spec.json --out out_dir
|
| 4 |
argv = sys.argv
|
| 5 |
argv = argv[argv.index("--")+1:] if "--" in argv else []
|
| 6 |
args = { argv[i].lstrip("-"): argv[i+1] for i in range(0, len(argv), 2) }
|
|
|
|
| 9 |
with open(spec_path, "r") as f:
|
| 10 |
spec = json.load(f)
|
| 11 |
|
| 12 |
+
name = spec.get("name", "Book")
|
| 13 |
sx, sy, sz = (spec["size_m"]["x"], spec["size_m"]["y"], spec["size_m"]["z"])
|
| 14 |
+
bevel = float(spec.get("bevel_m", 0.002))
|
| 15 |
+
corner_caps = bool(spec.get("corner_caps", True))
|
| 16 |
strap = spec.get("strap", {"enabled": False})
|
| 17 |
|
| 18 |
+
# --- clean scene
|
| 19 |
bpy.ops.wm.read_factory_settings(use_empty=True)
|
| 20 |
|
| 21 |
+
# --- build low poly: start from cube (2m cube at size=1.0)
|
| 22 |
mesh = bpy.data.meshes.new("book_lp")
|
| 23 |
obj = bpy.data.objects.new(name, mesh)
|
| 24 |
bpy.context.scene.collection.objects.link(obj)
|
| 25 |
bm = bmesh.new()
|
| 26 |
+
bmesh.ops.create_cube(bm, size=1.0)
|
| 27 |
bm.to_mesh(mesh); bm.free()
|
| 28 |
|
| 29 |
+
# scale to meters (cube at size=1 spans 2m, so use half the desired dims)
|
| 30 |
obj.scale = (sx/2, sy/2, sz/2)
|
| 31 |
bpy.context.view_layer.objects.active = obj
|
| 32 |
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
| 33 |
|
| 34 |
+
# bevel soft edges
|
| 35 |
bev = obj.modifiers.new("Bevel","BEVEL")
|
| 36 |
bev.width = bevel
|
| 37 |
bev.segments = 2
|
| 38 |
bev.limit_method = 'ANGLE'
|
| 39 |
bev.angle_limit = math.radians(60)
|
| 40 |
|
| 41 |
+
# simple spine loopcut (visual separation — minimal)
|
| 42 |
bpy.ops.object.mode_set(mode='EDIT')
|
| 43 |
bpy.ops.mesh.loopcut_slide(MESH_OT_loopcut={"number_cuts":1}, TRANSFORM_OT_edge_slide={"value":0.0})
|
| 44 |
bpy.ops.object.mode_set(mode='OBJECT')
|
| 45 |
|
| 46 |
# optional strap
|
| 47 |
if strap.get("enabled", False):
|
| 48 |
+
w = float(strap.get("width_m", 0.025))
|
| 49 |
bpy.ops.mesh.primitive_cube_add(size=1)
|
| 50 |
s = bpy.context.active_object; s.name = "strap"
|
| 51 |
s.scale = (sx*0.55, w/2, sz/2*1.05)
|
|
|
|
| 80 |
obj.data.use_auto_smooth = True
|
| 81 |
bpy.ops.object.shade_smooth()
|
| 82 |
|
| 83 |
+
# UV unwrap (keep simple for now)
|
| 84 |
bpy.ops.object.mode_set(mode='EDIT')
|
| 85 |
bpy.ops.uv.smart_project(angle_limit=66, island_margin=0.02)
|
| 86 |
bpy.ops.object.mode_set(mode='OBJECT')
|
| 87 |
|
| 88 |
+
# simple PBR material
|
| 89 |
mat = bpy.data.materials.new("MAT_Book")
|
| 90 |
mat.use_nodes = True
|
| 91 |
nt = mat.node_tree
|
| 92 |
bsdf = nt.nodes.get("Principled BSDF")
|
| 93 |
base = nt.nodes.new("ShaderNodeRGB")
|
| 94 |
+
leather = str(spec.get("style", {}).get("leather", "dark_brown"))
|
| 95 |
+
base.outputs[0].default_value = (0.12, 0.06, 0.03, 1.0) if "brown" in leather else (0.2, 0.2, 0.2, 1.0)
|
| 96 |
nt.links.new(base.outputs["Color"], bsdf.inputs["Base Color"])
|
| 97 |
bsdf.inputs["Roughness"].default_value = 0.6
|
| 98 |
bsdf.inputs["Metallic"].default_value = 0.0
|