
끌어서 회전, 휠·두 손가락으로 확대
이 모형에 대하여
파몰 H는 미국 인터내셔널 하베스터가 1939년부터 1953년까지 생산한 중형 로우크롭 트랙터다. 산업 디자이너 레이먼드 로위가 다듬은 둥근 라디에이터 덮개와 붉은 도장으로 이 시기 북미 농장의 대표적인 모습이 되었다.
로우크롭 트랙터는 옥수수·목화처럼 줄지어 심은 작물 사이를 지나가도록 만들어졌다. 앞바퀴 두 개를 한가운데로 모은 세발자전거형 앞차축이 이랑 하나 사이를 달리고, 키 큰 뒷바퀴가 양쪽 이랑을 넘는다. 엔진 옆면은 덮개 없이 드러나 있고, 긴 조향축이 보닛 위를 지나 앞 받침대의 기어함으로 이어진다.
이 모델은 이러한 H형의 특징, 곧 갈매기 무늬 러그가 박힌 큰 뒷타이어, 좁은 보닛 위로 솟은 배기관과 흡기 프리클리너, 둥근 펜더와 팬 모양 좌석, 오른쪽의 벨트 풀리를 실제 크기로 재현했다.
만든 방법
뒷타이어는 단면 프로필을 회전체로 돌린 뒤 비스듬한 상자 46개를 bmesh 로 붙여 갈매기 무늬 러그를 만들었고, 좌우 바퀴는 같은 메시를 공유한다. 보닛과 라디에이터 덮개는 위 모서리가 둥근 단면을 앞뒤로 돌출했고, 펜더는 뒷차축을 중심으로 한 원호 띠에 두께를 주었다. 조향 기둥과 핸들은 두 점 사이의 방향 벡터로 기울였다. 엔진은 블록·헤드·매니폴드·발전기·마그네토 등 큰 부품 위주로 단순화했고, 배선과 연결 링크, 유압 장치는 생략했다. 보닛 옆 글자는 3D 텍스트로 넣었다.
build.py
이 파일 하나를 Blender 에서 실행하면 위의 모형이 만들어집니다. 390줄.
# 로우크롭 트랙터 (1939~1953 파몰 H형) — Blender Python 코드만으로 만든 모델.# 큰 뒷바퀴와 앞쪽에 모인 쌍바퀴(세발자전거형), 좁고 긴 보닛, 보닛 위로 솟은 배기관·흡기관,# 보닛 위를 지나 앞 받침대까지 이어지는 긴 조향축이 이 형식을 알아보게 하는 특징이다.# 뒷타이어는 회전체(lathe) 단면에 갈매기(쉐브론) 러그 46개를 bmesh 로 붙여 만들고,# 보닛·라디에이터 덮개는 둥근 단면 돌출, 펜더는 원호 띠에 두께를 주어 만들었다.# 같은 바퀴·러그·볼트는 메시를 공유하는 복제로 배치했다. 단위 m, 앞쪽이 -Y.from atelier import *SHOT = {"azimuth": -40, "elevation": 13, "lens": 55, "light": "studio", "background": "#26303a"}# ---------------------------------------------------------------- 재질RED = mat('IH_Red_Paint', '#a51f18', 0.38, 0.0, coat=0.4)RUBBER = mat('Tire_Rubber', '#262626', 0.85, 0.0)IRON = mat('Cast_Iron_Dark', '#3b3836', 0.6, 0.5)STEEL = mat('Worn_Steel', '#8b8d8f', 0.35, 0.9)CHROME = mat('Chrome', '#d6d8da', 0.12, 1.0)EXHAUST = mat('Exhaust_Rust', '#4a3426', 0.75, 0.3)BLACK = mat('Black_Enamel', '#1f1f21', 0.4, 0.0)CREAM = mat('Cream_Lettering', '#e6dcc2', 0.45, 0.0)LENS = mat('Lamp_Lens', '#efe6c8', 0.08, 0.0, emit='#fff2c8', emit_strength=0.6)GLASSB = glass('Precleaner_Glass', '#c9d6d0', 0.08)# ---------------------------------------------------------------- 치수RA_Y, RA_Z = 0.95, 0.775 # 뒷차축 (y, z)REAR_X = 0.86 # 뒷바퀴 중심 xFA_Y, FA_Z = -1.33, 0.345 # 앞차축FRONT_X = 0.165 # 앞 쌍바퀴 간격 절반def obj_from_bm(name, bm, material, loc=(0, 0, 0), rot=(0, 0, 0), shade=True): me = bpy.data.meshes.new(name) bm.to_mesh(me) bm.free() ob = bpy.data.objects.new(name, me) link(ob) ob.location = Vector(loc) ob.rotation_euler = Euler(rot) assign(ob, material) if shade: smooth(ob) return obdef bm_lathe(bm, profile, seg, closed=False): """profile=[(r, z)] 를 Z축 회전 — 기존 bm 에 추가.""" rings = [] for i in range(seg): a = 2 * math.pi * i / seg나머지 342줄 펼치기접기
rings.append([bm.verts.new((r * math.cos(a), r * math.sin(a), z)) for r, z in profile]) n = len(profile) - (0 if closed else 1) for i in range(seg): r0, r1 = rings[i], rings[(i + 1) % seg] for j in range(n): k = (j + 1) % len(profile) bm.faces.new((r0[j], r1[j], r1[k], r0[k]))def rod(name, p0, p1, r, material, seg=16): """두 점을 잇는 원기둥.""" p0, p1 = Vector(p0), Vector(p1) d = p1 - p0 ob = cylinder(name, r=r, h=d.length, loc=p0, material=material, seg=seg) ob.rotation_euler = d.to_track_quat('Z', 'Y').to_euler() smooth(ob) return obdef rounded_profile(w, z0, z1, rc, n=8): """아래가 평평하고 위 모서리가 둥근 단면 (x, z).""" hw = w / 2 pts = [(-hw, z0), (hw, z0), (hw, z1 - rc)] for i in range(1, n): a = math.pi / 2 * i / n pts.append((hw - rc + rc * math.cos(a), z1 - rc + rc * math.sin(a))) for i in range(0, n + 1): a = math.pi / 2 + math.pi / 2 * i / n pts.append((-hw + rc + rc * math.cos(a), z1 - rc + rc * math.sin(a))) return pts# ---------------------------------------------------------------- 뒷바퀴def rear_tire(): bm = bmesh.new() prof = [(0.495, 0.118), (0.56, 0.138), (0.64, 0.146), (0.695, 0.132), (0.722, 0.095), (0.732, 0.04), (0.734, 0.0), (0.732, -0.04), (0.722, -0.095), (0.695, -0.132), (0.64, -0.146), (0.56, -0.138), (0.495, -0.118)] bm_lathe(bm, prof, 120) N = 23 for side in (1, -1): for i in range(N): th = 2 * math.pi * i / N + (math.pi / N if side < 0 else 0.0) m = (Matrix.Rotation(th, 4, 'Z') @ Matrix.Translation((0.738, 0, side * 0.066)) @ Matrix.Rotation(math.radians(38) * side, 4, 'X') @ Matrix.Diagonal((0.075, 0.05, 0.165, 1.0))) res = bmesh.ops.create_cube(bm, size=1.0, matrix=m) bmesh.ops.recalc_face_normals(bm, faces=bm.faces) return obj_from_bm('Rear_Tire_11-38', bm, RUBBER)def rear_rim(): bm = bmesh.new() prof = [(0.525, 0.14), (0.50, 0.125), (0.488, 0.10), (0.488, -0.10), (0.50, -0.125), (0.525, -0.14), (0.515, -0.145), (0.478, -0.11), (0.478, 0.11), (0.515, 0.145)] bm_lathe(bm, prof, 96, closed=True) ob = obj_from_bm('Rear_Rim', bm, RED) # 오목한 휠 디스크 disc = lathe('Rear_Wheel_Disc', [(0.48, 0.03), (0.40, 0.05), (0.26, 0.075), (0.12, 0.075), (0.12, 0.06), (0.26, 0.06), (0.40, 0.035), (0.48, 0.015)], material=RED, seg=64) hub = cylinder('Rear_Hub', r=0.13, h=0.16, loc=(0, 0, 0.02), material=RED, seg=32) cap = cylinder('Rear_Axle_End', r=0.075, h=0.08, loc=(0, 0, 0.18), material=STEEL, seg=24) parts = [ob, disc, hub, cap] bolt = cylinder('Rear_Wheel_Bolt', r=0.016, h=0.03, loc=(0.2, 0, 0.07), material=STEEL, seg=8) parts.append(bolt) for i in range(1, 8): a = 2 * math.pi * i / 8 parts.append(duplicate(bolt, loc=(0.2 * math.cos(a), 0.2 * math.sin(a), 0.07), linked=False)) # 디스크 보강 리브 for i in range(6): a = 2 * math.pi * (i + 0.5) / 6 rb = box('Rear_Disc_Rib', (0.24, 0.035, 0.03), loc=(0.32 * math.cos(a), 0.32 * math.sin(a), 0.055), rot=(0, math.radians(-12), a), material=RED) parts.append(rb) return join(parts, 'Rear_Wheel_Rim')tire = rear_tire()rim = rear_rim()for sx, nm in ((1, 'Right'), (-1, 'Left')): t = tire if sx == 1 else duplicate(tire, name='Rear_Tire_Left') t.location = (sx * REAR_X, RA_Y, RA_Z) t.rotation_euler = (0, math.pi / 2, 0) if sx == 1: t.name = 'Rear_Tire_Right' r = rim if sx == 1 else duplicate(rim, name='Rear_Wheel_Left') r.location = (sx * REAR_X, RA_Y, RA_Z) r.rotation_euler = (0, sx * math.pi / 2, 0) if sx == 1: r.name = 'Rear_Wheel_Right'# ---------------------------------------------------------------- 앞 쌍바퀴def front_tire(): bm = bmesh.new() prof = [(0.215, 0.055), (0.25, 0.068), (0.29, 0.07), (0.318, 0.06), (0.333, 0.045), (0.343, 0.036), (0.345, 0.026), (0.336, 0.018), (0.336, 0.010), (0.345, 0.004), (0.345, -0.004), (0.336, -0.010), (0.336, -0.018), (0.345, -0.026), (0.343, -0.036), (0.333, -0.045), (0.318, -0.06), (0.29, -0.07), (0.25, -0.068), (0.215, -0.055)] bm_lathe(bm, prof, 72) return obj_from_bm('Front_Tire_5.50-16', bm, RUBBER)def front_rim(): rimb = lathe('Front_Rim', [(0.225, 0.062), (0.205, 0.05), (0.205, -0.05), (0.225, -0.062), (0.215, -0.066), (0.196, -0.05), (0.196, 0.05), (0.215, 0.066)], material=RED, seg=48) disc = lathe('Front_Wheel_Disc', [(0.2, 0.0), (0.12, 0.03), (0.07, 0.03), (0.07, 0.018), (0.12, 0.018), (0.2, -0.012)], material=RED, seg=40) hub = lathe('Front_Hub_Cap', [(0.07, 0.0), (0.07, 0.06), (0.05, 0.085), (0.0, 0.09)], material=RED, seg=32) return join([rimb, disc, hub], 'Front_Wheel_Rim')ft = front_tire()fr = front_rim()for sx in (1, -1): nm = 'Right' if sx == 1 else 'Left' t = ft if sx == 1 else duplicate(ft) t.name = f'Front_Tire_{nm}' t.location = (sx * FRONT_X, FA_Y, FA_Z) t.rotation_euler = (0, math.pi / 2, 0) w = fr if sx == 1 else duplicate(fr) w.name = f'Front_Wheel_{nm}' w.location = (sx * FRONT_X, FA_Y, FA_Z) w.rotation_euler = (0, sx * math.pi / 2, 0)# 앞 받침대(페데스털)와 짧은 앞차축rod('Front_Axle', (-0.13, FA_Y, FA_Z), (0.13, FA_Y, FA_Z), 0.04, IRON)fork = box('Front_Fork_Yoke', (0.2, 0.13, 0.16), loc=(0, FA_Y, FA_Z + 0.03), material=RED, base=True)bevel(fork, 0.02, 2)rod('Front_Steering_Spindle', (0, FA_Y + 0.02, FA_Z + 0.17), (0, -1.27, 0.76), 0.062, RED)ped = extrude_profile('Front_Bolster_Pedestal', [(-0.08, 0.50), (0.08, 0.50), (0.17, 0.74), (-0.17, 0.74)], 0.2, loc=(0, -1.28, 0), material=RED)bevel(ped, 0.02, 2)# ---------------------------------------------------------------- 엔진·차대rail_l = box('Frame_Rail_Left', (0.07, 1.62, 0.13), loc=(-0.19, -0.42, 0.57), material=RED, base=True)rail_r = duplicate(rail_l, loc=(0.19, -0.42, 0.57), name='Frame_Rail_Right')bolster = box('Front_Bolster', (0.46, 0.2, 0.12), loc=(0, -1.25, 0.62), material=RED, base=True)bevel(bolster, 0.015, 2)pan = box('Engine_Oil_Pan', (0.26, 0.95, 0.12), loc=(0, -0.5, 0.56), material=RED, base=True)bevel(pan, 0.03, 2)block = box('Engine_Block_C152', (0.27, 1.02, 0.42), loc=(0, -0.53, 0.68), material=RED, base=True)bevel(block, 0.02, 2)head = box('Cylinder_Head', (0.25, 0.98, 0.07), loc=(0, -0.53, 1.10), material=RED, base=True)bevel(head, 0.012, 2)# 측면 상세: 흡배기 매니폴드, 기화기, 마그네토, 시동모터man = rod('Exhaust_Manifold', (0.17, -0.95, 1.0), (0.17, -0.1, 1.0), 0.04, EXHAUST)for i, y in enumerate((-0.86, -0.64, -0.42, -0.2)): rod('Manifold_Port', (0.14, y, 1.0), (0.18, y, 1.0), 0.032, EXHAUST, seg=12)carb = box('Carburetor', (0.09, 0.12, 0.14), loc=(0.22, -0.52, 0.86), material=IRON, base=True)bevel(carb, 0.01, 1)rod('Intake_Pipe', (0.17, -0.52, 0.99), (0.24, -0.52, 0.99), 0.03, IRON)mag = rod('Magneto', (-0.17, -0.70, 0.88), (-0.26, -0.70, 0.88), 0.06, BLACK, seg=20)rod('Starter_Motor', (-0.17, -0.02, 0.78), (-0.17, -0.32, 0.78), 0.06, BLACK, seg=20)oilf = cylinder('Oil_Filter', r=0.055, h=0.17, loc=(-0.2, -0.35, 0.84), material=RED, seg=20)for i in range(4): y = -0.86 + i * 0.22 rod(f'Spark_Plug_Wire_{i + 1}', (-0.13, y, 1.12), (-0.2, -0.70, 0.94), 0.007, BLACK, seg=6) cylinder(f'Spark_Plug_{i + 1}', r=0.012, h=0.05, loc=(-0.13, y, 1.08), material=CREAM, seg=8)# 블록 측면: 냉각수 플러그, 밸브 덮개(왼쪽), 냉각 호스, 팬for i in range(3): for sx in (1, -1): cylinder('Core_Plug', r=0.025, h=0.012, loc=(sx * 0.135, -0.86 + i * 0.3, 0.9), rot=(0, sx * math.pi / 2, 0), material=STEEL, seg=12, base=False)vc = box('Valve_Chamber_Cover', (0.02, 0.62, 0.12), loc=(-0.14, -0.5, 0.94), material=RED)bevel(vc, 0.006, 1)for i in range(4): cylinder('Cover_Bolt', r=0.01, h=0.012, loc=(-0.152, -0.74 + i * 0.16, 0.94), rot=(0, -math.pi / 2, 0), material=STEEL, seg=8, base=False)rod('Upper_Radiator_Hose', (0, -1.04, 1.12), (0, -1.16, 1.18), 0.035, BLACK, seg=12)lathe('Water_Outlet', [(0.0, 0.0), (0.05, 0.0), (0.05, 0.08), (0.0, 0.08)], loc=(0, -0.98, 1.10), rot=(math.pi / 2, 0, 0), material=RED, seg=16)rod('Lower_Radiator_Hose', (0.1, -1.04, 0.76), (0.1, -1.16, 0.76), 0.03, BLACK, seg=12)rod('Fan_Shaft', (0, -1.04, 0.96), (0, -1.13, 0.96), 0.03, IRON, seg=12)for i in range(4): a = math.pi / 4 + i * math.pi / 2 box('Fan_Blade', (0.18, 0.012, 0.07), loc=(0.11 * math.cos(a), -1.115, 0.96 + 0.11 * math.sin(a)), rot=(math.radians(25), a, 0), material=IRON)cylinder('Governor_Housing', r=0.06, h=0.08, loc=(0.0, -1.05, 0.70), rot=(math.pi / 2, 0, 0), material=RED, seg=20, base=False)rod('Generator', (0.2, -0.98, 0.84), (0.2, -0.72, 0.84), 0.065, BLACK, seg=20)rod('Generator_Pulley', (0.2, -1.02, 0.84), (0.2, -0.98, 0.84), 0.045, IRON, seg=16)rod('Fuel_Line', (0.06, 0.05, 1.11), (0.22, -0.46, 0.99), 0.008, STEEL, seg=6)# 라디에이터rad = box('Radiator_Core', (0.48, 0.12, 0.62), loc=(0, -1.2, 0.72), material=IRON, base=True)# 클러치 하우징 (엔진 → 변속기)lathe('Clutch_Housing', [(0.0, 0.0), (0.2, 0.0), (0.26, 0.2), (0.0, 0.2)], loc=(0, -0.05, 0.86), rot=(-math.pi / 2, 0, 0), material=RED, seg=40)# 벨트 풀리 (오른쪽)lathe('Belt_Pulley', [(0.0, 0.0), (0.15, 0.0), (0.16, 0.01), (0.16, 0.15), (0.15, 0.16), (0.0, 0.16)], loc=(0.27, 0.07, 0.86), rot=(0, math.pi / 2, 0), material=RED, seg=48)rod('Pulley_Shaft', (0.18, 0.07, 0.86), (0.28, 0.07, 0.86), 0.05, IRON)# 변속기·차동장치 케이스trans = box('Transmission_Case', (0.58, 0.78, 0.56), loc=(0, 0.6, 0.52), material=RED, base=True)bevel(trans, 0.05, 3)for sx in (1, -1): rod('Rear_Axle_Housing', (sx * 0.28, RA_Y, RA_Z), (sx * 0.74, RA_Y, RA_Z), 0.09, RED, seg=24) rod('Final_Drive_Flange', (sx * 0.28, RA_Y, RA_Z), (sx * 0.33, RA_Y, RA_Z), 0.15, RED, seg=32) db = box('Brake_Housing', (0.1, 0.3, 0.3), loc=(sx * 0.34, 0.55, 0.78), material=RED) bevel(db, 0.04, 3) # 발판 fb = box('Foot_Platform', (0.34, 0.5, 0.025), loc=(sx * 0.48, 0.42, 0.66), material=STEEL, base=True) for k in range(5): box('Platform_Tread_Bar', (0.3, 0.02, 0.012), loc=(sx * 0.48, 0.22 + k * 0.1, 0.684), material=STEEL, base=True) rod('Platform_Bracket', (sx * 0.3, 0.6, 0.67), (sx * 0.3, 0.6, 0.6), 0.02, IRON)for i, sx in enumerate((0.07, -0.07)): rod('Brake_Pedal_Arm', (0.3 + 0.03 * i, 0.5, 0.78), (0.4 + 0.03 * i, 0.32, 0.86), 0.015, IRON, seg=8) box('Brake_Pedal_Pad', (0.07, 0.04, 0.09), loc=(0.4 + 0.03 * i, 0.32, 0.86), material=IRON)rod('Clutch_Pedal_Arm', (-0.3, 0.45, 0.78), (-0.42, 0.3, 0.86), 0.015, IRON, seg=8)box('Clutch_Pedal_Pad', (0.07, 0.04, 0.09), loc=(-0.42, 0.3, 0.86), material=IRON)# 견인봉(드로바)db = box('Drawbar', (0.62, 0.08, 0.04), loc=(0, 1.32, 0.42), material=IRON)bevel(db, 0.01, 1)for sx in (1, -1): rod('Drawbar_Strut', (sx * 0.28, 0.85, 0.55), (sx * 0.28, 1.32, 0.43), 0.025, IRON, seg=10)for k in range(5): cylinder('Drawbar_Hole_Pin', r=0.012, h=0.05, loc=(-0.2 + k * 0.1, 1.32, 0.415), material=STEEL, seg=8)rc = box('Rear_Cover_Plate', (0.44, 0.02, 0.4), loc=(0, 0.995, 0.58), material=RED, base=True)bevel(rc, 0.008, 1)for i in range(6): for zz in (0.62, 0.95): cylinder('Rear_Cover_Bolt', r=0.012, h=0.015, loc=(-0.19 + i * 0.076, 1.005, zz), rot=(-math.pi / 2, 0, 0), material=STEEL, seg=8, base=False)box('PTO_Shield', (0.18, 0.12, 0.1), loc=(0, 1.02, 0.6), material=RED)rod('PTO_Shaft', (0, 0.98, 0.6), (0, 1.12, 0.6), 0.025, STEEL, seg=12)# ---------------------------------------------------------------- 보닛·라디에이터 덮개HOOD_Y0, HOOD_Y1 = -1.16, 0.30hood = extrude_profile('Hood', rounded_profile(0.5, 1.12, 1.42, 0.16), HOOD_Y1 - HOOD_Y0, loc=(0, (HOOD_Y0 + HOOD_Y1) / 2, 0), material=RED)smooth(hood)bevel(hood, 0.01, 2, angle=50)shell = extrude_profile('Radiator_Shell', rounded_profile(0.58, 0.72, 1.45, 0.2, 10), 0.16, loc=(0, -1.25, 0), material=RED)smooth(shell)bevel(shell, 0.012, 2, angle=50)# 그릴 — 짙은 바탕판 위 가로 살box('Grille_Backing', (0.46, 0.012, 0.6), loc=(0, -1.334, 0.79), material=IRON, base=True)for k in range(12): z = 0.82 + k * 0.047 gb = box('Grille_Bar', (0.44, 0.02, 0.02), loc=(0, -1.345, z), material=RED)box('Grille_Center_Bar', (0.035, 0.024, 0.58), loc=(0, -1.347, 0.80), material=RED, base=True)# 보닛 옆 상표 띠 (크림색 줄)for sx in (1, -1): box('Hood_Stripe', (0.004, 1.2, 0.012), loc=(sx * 0.2505, -0.42, 1.2), material=CREAM) t = text3d('Hood_Lettering', 'FARMALL', size=0.085, depth=0.003, loc=(sx * 0.252, -0.55, 1.23), rot=(math.pi / 2, 0, sx * math.pi / 2), material=CREAM) if sx < 0: t.rotation_euler = (math.pi / 2, 0, -math.pi / 2)# 계기판 / 연료탱크 마감dash = extrude_profile('Dash_Panel', rounded_profile(0.5, 1.02, 1.42, 0.16), 0.03, loc=(0, HOOD_Y1 + 0.015, 0), material=RED)smooth(dash)cylinder('Fuel_Cap', r=0.05, h=0.05, loc=(0, -0.05, 1.41), material=STEEL, seg=20)cylinder('Radiator_Cap', r=0.04, h=0.05, loc=(0.08, -1.25, 1.44), material=STEEL, seg=20)gauge = cylinder('Ammeter_Gauge', r=0.035, h=0.02, loc=(0.12, HOOD_Y1 + 0.03, 1.3), rot=(-math.pi / 2, 0, 0), material=CREAM, seg=20)# 배기관(오른쪽)과 흡기 프리클리너(가운데)cylinder('Muffler', r=0.06, h=0.42, loc=(0.12, -0.55, 1.38), material=EXHAUST, seg=24)cylinder('Exhaust_Stack', r=0.035, h=0.38, loc=(0.12, -0.55, 1.79), material=EXHAUST, seg=16)cap = cylinder('Exhaust_Rain_Cap', r=0.05, h=0.012, loc=(0.135, -0.55, 2.17), rot=(0, math.radians(-25), 0), material=EXHAUST, seg=20)cylinder('Air_Intake_Pipe', r=0.045, h=0.25, loc=(0.0, -0.95, 1.38), material=RED, seg=20)cylinder('Precleaner_Base', r=0.07, h=0.03, loc=(0.0, -0.95, 1.63), material=STEEL, seg=24)cylinder('Precleaner_Bowl', r=0.065, h=0.11, loc=(0.0, -0.95, 1.66), material=GLASSB, seg=24)lathe('Precleaner_Cap', [(0.0, 0.0), (0.075, 0.0), (0.075, 0.02), (0.05, 0.05), (0.0, 0.055)], loc=(0, -0.95, 1.77), material=STEEL, seg=24)# 전조등 (라디에이터 덮개 옆)for sx in (1, -1): rod('Headlight_Bracket', (sx * 0.29, -1.25, 1.2), (sx * 0.37, -1.25, 1.2), 0.012, IRON, seg=8) lathe('Headlight_Body', [(0.0, 0.0), (0.05, 0.0), (0.075, 0.05), (0.08, 0.1), (0.0, 0.1)], loc=(sx * 0.4, -1.2, 1.2), rot=(math.pi / 2, 0, 0), material=RED, seg=24) cylinder('Headlight_Lens', r=0.07, h=0.01, loc=(sx * 0.4, -1.3, 1.2), rot=(math.pi / 2, 0, 0), material=LENS, seg=24) torus('Headlight_Bezel', 0.075, 0.008, loc=(sx * 0.4, -1.302, 1.2), rot=(math.pi / 2, 0, 0), material=CHROME, seg=24, rseg=6)# ---------------------------------------------------------------- 조향 장치WHEEL_C = Vector((0.0, 0.86, 1.70))UJ = Vector((-0.13, 0.32, 1.50))col_dir = (WHEEL_C - UJ).normalized()rod('Steering_Column', UJ, WHEEL_C, 0.022, IRON)rod('Steering_Shaft', (-0.13, -1.22, 1.50), UJ, 0.02, IRON)sphere('Steering_U_Joint', 0.035, loc=UJ, material=IRON, seg=16, rings=8)gb = box('Steering_Gear_Box', (0.12, 0.14, 0.12), loc=(-0.13, -1.25, 1.42), material=RED, base=True)bevel(gb, 0.02, 2)for y in (-0.55, 0.05): rod('Shaft_Support', (-0.13, y, 1.40), (-0.13, y, 1.49), 0.012, IRON, seg=8)q = col_dir.to_track_quat('Z', 'Y')sw_rim = torus('Steering_Wheel_Rim', 0.21, 0.016, material=BLACK, seg=48, rseg=10)sw_hub = cylinder('Steering_Wheel_Hub', r=0.04, h=0.06, loc=(0, 0, -0.03), material=BLACK, seg=16)spokes = [sw_rim, sw_hub]for i in range(3): a = 2 * math.pi * i / 3 + math.pi / 2 s = rod('Steering_Spoke', (0, 0, 0), (0.2 * math.cos(a), 0.2 * math.sin(a), -0.012), 0.011, STEEL, seg=8) spokes.append(s)sw = join(spokes, 'Steering_Wheel')sw.location = WHEEL_Csw.rotation_euler = q.to_euler()# ---------------------------------------------------------------- 좌석leaf = rod('Seat_Spring_Bar', (0, 0.80, 1.07), (0, 1.36, 1.15), 0.03, IRON)seat = lathe('Pan_Seat', [(0.0, 0.0), (0.16, 0.005), (0.2, 0.03), (0.225, 0.09), (0.215, 0.095), (0.19, 0.04), (0.155, 0.018), (0.0, 0.013)], loc=(0, 1.38, 1.16), material=STEEL, seg=48)seat.scale = (1.0, 0.9, 1.0)seat.rotation_euler = (math.radians(-8), 0, 0)rod('Seat_Post', (0, 1.36, 1.10), (0, 1.38, 1.18), 0.04, IRON)# ---------------------------------------------------------------- 펜더def fender(name, sx): bm = bmesh.new() a0, a1, n = math.radians(12), math.radians(150), 40 R = 0.87 xs = [(0.70, 0.0), (0.98, 0.0), (1.02, -0.045)] rows = [] for i in range(n + 1): a = a0 + (a1 - a0) * i / n row = [] for x, dr in xs: rr = R + dr row.append(bm.verts.new((sx * x, RA_Y + rr * math.cos(a), RA_Z + rr * math.sin(a)))) rows.append(row) for i in range(n): for j in range(len(xs) - 1): bm.faces.new((rows[i][j], rows[i][j + 1], rows[i + 1][j + 1], rows[i + 1][j])) ob = obj_from_bm(name, bm, RED) solidify(ob, 0.008) return obfor sx in (1, -1): fender('Rear_Fender', sx) # 펜더 지지대: 차축 하우징에서 펜더 안쪽으로 for a in (60, 120): ar = math.radians(a) rod('Fender_Brace', (sx * 0.66, RA_Y + 0.12 * math.cos(ar), RA_Z + 0.12 * math.sin(ar)), (sx * 0.72, RA_Y + 0.865 * math.cos(ar), RA_Z + 0.865 * math.sin(ar)), 0.018, RED, seg=8)






