58 lines
4.7 KiB
Python
58 lines
4.7 KiB
Python
import bpy, math
|
|
from mathutils import Vector
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(r'C:\projects\wheel')
|
|
OUT = ROOT / 'models' / 'turn_signal_cam_concept'
|
|
OUT.mkdir(parents=True, exist_ok=True)
|
|
bpy.ops.object.select_all(action='SELECT'); bpy.ops.object.delete(use_global=False)
|
|
|
|
def mat(name, color, metallic=0):
|
|
m=bpy.data.materials.new(name); m.diffuse_color=(*color,1); m.use_nodes=True
|
|
p=m.node_tree.nodes.get('Principled BSDF'); p.inputs['Base Color'].default_value=(*color,1); p.inputs['Metallic'].default_value=metallic; p.inputs['Roughness'].default_value=.4
|
|
return m
|
|
blue=mat('Ring upper',(.055,.33,.7)); blue2=mat('Ring lower',(.08,.48,.8)); steel=mat('Shaft',(.36,.4,.43),.7); brass=mat('Key',(.93,.58,.13),.6); boltmat=mat('Bolts',(.23,.25,.28),.7); cammat=blue
|
|
def box(name,location,scale,material=None):
|
|
bpy.ops.mesh.primitive_cube_add(size=1,location=location); o=bpy.context.object;o.name=name;o.dimensions=scale;bpy.ops.object.transform_apply(location=False,rotation=False,scale=True)
|
|
if material:o.data.materials.append(material)
|
|
return o
|
|
def boolean(obj,other,op):
|
|
bpy.context.view_layer.objects.active=obj; mod=obj.modifiers.new('shape', 'BOOLEAN');mod.operation=op;mod.object=other;bpy.ops.object.modifier_apply(modifier=mod.name);bpy.data.objects.remove(other,do_unlink=True)
|
|
def half(name,a0,a1,color):
|
|
n=80; verts=[]; faces=[]
|
|
for z in (0,8):
|
|
for radius in (6,13):
|
|
for i in range(n+1):
|
|
a=math.radians(a0+(a1-a0)*i/n);verts.append((radius*math.cos(a),radius*math.sin(a),z))
|
|
off=n+1
|
|
for i in range(n):
|
|
faces += [(i,i+1,off+i+1,off+i), (2*off+i,3*off+i,3*off+i+1,2*off+i+1), (i,2*off+i,2*off+i+1,i+1), (off+i,off+i+1,3*off+i+1,3*off+i)]
|
|
faces.extend([(0,off,3*off,2*off),(n,2*off+n,3*off+n,off+n)])
|
|
mesh=bpy.data.meshes.new(name);mesh.from_pydata(verts,[],faces);mesh.update();o=bpy.data.objects.new(name,mesh);bpy.context.collection.objects.link(o);o.data.materials.append(color)
|
|
return o
|
|
top=half('Upper half with keyway',2,178,blue);bot=half('Lower half',182,358,blue2)
|
|
boolean(top,box('slot',(0,6.5,4),(4.1,3.6,8.2)),'DIFFERENCE')
|
|
# Each clamp ear overlaps the relevant semicircle. The ear screw passages run along Y.
|
|
for side in (-1,1):
|
|
for ring,y in ((top,2.65),(bot,-2.65)):
|
|
boolean(ring,box('ear',(side*12.8,y,4),(6.0,5.0,8)),'UNION')
|
|
# Head and shank are illustrative, separate parts.
|
|
bpy.ops.mesh.primitive_cylinder_add(vertices=24,radius=1.4,depth=12,location=(side*13.1,0,4),rotation=(math.pi/2,0,0));bpy.context.object.name='M3 shank schematic';bpy.context.object.data.materials.append(boltmat)
|
|
bpy.ops.mesh.primitive_cylinder_add(vertices=6,radius=2.4,depth=2,location=(side*13.1,6,4),rotation=(math.pi/2,0,0));bpy.context.object.name='M3 bolt head schematic';bpy.context.object.data.materials.append(boltmat)
|
|
bpy.ops.mesh.primitive_cylinder_add(vertices=6,radius=2.4,depth=2,location=(side*13.1,-6,4),rotation=(math.pi/2,0,0));bpy.context.object.name='M3 nut schematic';bpy.context.object.data.materials.append(boltmat)
|
|
# The protrusion overlaps the top semicircular ring before a union: one continuous piece.
|
|
boolean(top,box('cam',(0,15,4),(8,7,8)),'UNION')
|
|
bpy.ops.mesh.primitive_cylinder_add(vertices=96,radius=6,depth=28,location=(0,0,4));shaft=bpy.context.object;shaft.name='12mm shaft';shaft.data.materials.append(steel)
|
|
boolean(shaft,box('shaft keyway',(0,6.2,4),(4.1,3.9,28.1)),'DIFFERENCE')
|
|
key=box('4x4mm metal key',(0,6.2,5),(4,4,12),brass)
|
|
# ground plane and soft lighting
|
|
ground=box('Ground',(0,0,-12.5),(180,180,1),mat('Ground',(.97,.975,.985)))
|
|
world=bpy.context.scene.world;world.color=(.8,.8,.8)
|
|
def light(loc,power,size):
|
|
bpy.ops.object.light_add(type='AREA',location=loc);o=bpy.context.object;o.data.energy=power;o.data.shape='DISK';o.data.size=size;o.rotation_euler=(Vector((0,0,0))-o.location).to_track_quat('-Z','Y').to_euler()
|
|
light((5,10,65),25000,45);light((-30,-10,40),15000,35)
|
|
bpy.ops.object.camera_add();camera=bpy.context.object;bpy.context.scene.camera=camera;camera.data.type='ORTHO';camera.data.ortho_scale=67
|
|
scene=bpy.context.scene;scene.render.engine='CYCLES';scene.cycles.samples=24;scene.render.resolution_x=1200;scene.render.resolution_y=900;scene.render.resolution_percentage=100;scene.render.image_settings.file_format='PNG';scene.world.color=(.8,.8,.8)
|
|
camera.location=(43,60,58);camera.rotation_euler=(Vector((0,2,4))-camera.location).to_track_quat('-Z','Y').to_euler();scene.render.filepath=str(OUT/'perspective.png');bpy.ops.render.render(write_still=True)
|
|
camera.location=(0,0,75);camera.rotation_euler=(0,0,0);camera.rotation_euler=(Vector((0,0,0))-camera.location).to_track_quat('-Z','Y').to_euler();camera.data.ortho_scale=52;scene.render.filepath=str(OUT/'front.png');bpy.ops.render.render(write_still=True)
|