본문으로 건너뛰기
3D 아틀리에
엘리자베스 타워 (빅벤) 3D 모형

끌어서 회전, 휠·두 손가락으로 확대

이 모형에 대하여

엘리자베스 타워는 1834년 화재로 소실된 웨스트민스터 궁전을 재건하면서 세운 시계탑이다. 건축가 찰스 배리가 궁전 전체를 설계했고, 탑의 설계는 오거스터스 퓨진이 맡았다. 1859년 완공되었으며, 원래 '시계탑'으로 불리다가 2012년 엘리자베스 2세 즉위 60주년을 기념해 지금의 이름이 붙었다. 흔히 부르는 '빅벤'은 본래 탑 안에 걸린 가장 큰 종의 별칭이다.

탑은 벽돌 구조에 앤스턴 석회암을 덧댔고, 위쪽 지붕과 첨탑은 주철로 덮었다. 네 면에는 지름 약 7m의 시계판이 있으며, 주철 틀에 유백색 유리를 끼워 넣은 구조다. 시계판은 금박 장식이 든 정사각 테두리 안에 놓이고, 그 위로 종루와 가파른 사각뿔 지붕, 의회 개회 중에 불을 밝히는 에어턴 라이트 랜턴이 이어진다.

이 모델은 실제 비례를 따라 탑신, 시계층, 종루, 지붕, 랜턴, 첨탑을 쌓아 올렸다. 탑신의 수직 패널과 뾰족아치, 시계판의 로마 숫자와 방사형 철골, 2017~2022년 보수 때 되살린 프러시안 블루 철골과 금박 장식의 배색을 표현했다.

만든 방법

한 면(-Y)의 패널·창·시계판을 만들어 하나의 메시로 합친 뒤, 탑 중심축으로 90°씩 돌린 링크 복제로 나머지 세 면을 채웠다. 뾰족아치 테두리와 창은 원호 두 개로 계산한 2D 윤곽을 돌출해 만들었고, 시계판은 원판·토러스 고리·방사형 살·텍스트를 메시로 바꾼 로마 숫자로 구성했다. 지붕과 첨탑, 시계층 아래 내민 받침은 4분할 회전체를 45° 돌려 사각뿔대로 만들었다. 실제 탑신의 세밀한 퍼펜디큘러 트레이서리와 조각, 시계판 아래 라틴어 명문은 단순한 패널과 금박 띠로 줄였고, 궁전 본관과 이어지는 부분은 생략해 탑만 독립시켰다.

build.py

이 파일 하나를 Blender 에서 실행하면 위의 모형이 만들어집니다. 329줄.

# 엘리자베스 타워(빅벤), 영국 런던 웨스트민스터 궁전 — 높이 약 96m 실측 비례 모델.
# 정사각 탑신·시계층·종루·주철 지붕·랜턴·첨탑을 아래에서 위로 쌓았다.
# 한 면(-Y 쪽)의 패널·아치·시계판을 만들어 하나로 합친 뒤, 탑 중심축으로 90°씩 돌린
# 링크 복제로 나머지 세 면을 채웠다. 시계판은 원판 + 토러스 고리 + 방사형 살 + 로마 숫자(텍스트→메시),
# 뾰족아치 테두리는 2D 윤곽을 돌출(extrude)해 만들고, 지붕과 첨탑은 4분할 회전체로 사각뿔을 냈다.
import math
from atelier import *
SHOT = {"azimuth": -58, "elevation": 9, "lens": 60, "light": "day", "background": "#26303d"}
# ── 재질 ────────────────────────────────────────────────
STONE = mat('Anston_Limestone', '#cdb88f', 0.8)
RECESS = mat('Limestone_Shadowed', '#a8916b', 0.85)
PLINTH = mat('Plinth_Stone', '#a59479', 0.85)
DARK = mat('Belfry_Interior', '#2a2622', 0.9)
WIN = mat('Window_Glass', '#2e3742', 0.2, 0.1)
DIAL = mat('Opal_Dial_Glass', '#efe6cf', 0.35, emit='#fff1d0', emit_strength=0.25)
IRON = mat('Prussian_Blue_Iron', '#1d2a48', 0.4, 0.5)
GOLD = mat('Gold_Leaf', '#d4a84a', 0.28, 1.0)
ROOF = mat('Cast_Iron_Roof', '#3a4652', 0.5, 0.45)
LAMP = mat('Ayrton_Light_Glass', '#ead9a6', 0.3, emit='#ffe2a0', emit_strength=0.6)
H = 6.0 # 탑신 반폭(12m 정사각)
SQ = math.sqrt(2)
HB = 6.4 # 버트레스·시계층 반폭
def face_group(objs, name):
"""한 면 부품을 합치고, 원점을 탑 중심축에 둔 뒤 나머지 세 면을 링크 복제."""
ob = join(objs, name)
ob.data.transform(ob.matrix_world)
ob.matrix_world = Matrix.Identity(4)
for k in (1, 2, 3):
duplicate(ob, loc=(0, 0, 0), rot=(0, 0, k * math.pi / 2), name=f'{name}_{k}')
return ob
def arch_curve(w, rise, n=8):
"""폭 w, 높이 rise 인 뾰족아치 곡선 점(왼쪽 밑→정점→오른쪽 밑), 기준 z=0."""
a = w / 2
R = (a * a + rise * rise) / (2 * a) # 정점 높이가 rise 가 되는 반지름
cx = -a + R
left = []
for i in range(n + 1):
t = i / n
ang0 = math.pi
ang1 = math.pi - math.asin(rise / R)
ang = ang0 + (ang1 - ang0) * t
나머지 281줄 펼치기
left.append((cx + R * math.cos(ang), R * math.sin(ang)))
left[-1] = (0.0, rise)
right = [(-x, z) for x, z in reversed(left[:-1])]
return left + right
def arch_spandrel(name, w, rise, top, x, z, y, depth, material):
"""패널 윗부분: 사각형에서 뾰족아치를 빼낸 돌 테두리."""
pts = arch_curve(w, rise) + [(w / 2, top), (-w / 2, top)]
ob = extrude_profile(name, pts, depth, loc=(x, y, z), material=material)
return ob
def lancet(name, w, h, rise, x, z, y, depth, material):
"""직사각 + 뾰족아치 머리의 창(창유리 판)."""
arc = arch_curve(w, rise)
pts = [(-w / 2, -h)] + [(px, pz) for px, pz in arc] + [(w / 2, -h)]
pts = list(reversed(pts))
return extrude_profile(name, pts, depth, loc=(x, y, z + h), material=material)
def sq_lathe(name, profile, material):
"""4분할 회전체를 45° 돌려 사각뿔(대)로 만들고, 회전을 메시에 굳힌다. profile 의 r 은 반폭."""
ob = lathe(name, [(r * SQ, z) for r, z in profile], material=material, seg=4, shade_smooth=False)
ob.data.transform(Matrix.Rotation(math.pi / 4, 4, 'Z'))
return ob
def text_mesh(name, body, size, depth, material):
t = text3d(name + '_txt', body, size=size, depth=depth, material=material)
t.data.align_y = 'CENTER'
dg = bpy.context.evaluated_depsgraph_get()
me = bpy.data.meshes.new_from_object(t.evaluated_get(dg))
bpy.data.objects.remove(t, do_unlink=True)
ob = bpy.data.objects.new(name, me)
link(ob)
assign(ob, material)
return ob
# ── 기단과 탑신 몸체 ──────────────────────────────────────
box('Plinth', (13.4, 13.4, 2.5), material=PLINTH, base=True)
box('Plinth_Cap', (13.6, 13.6, 0.35), loc=(0, 0, 2.5), material=STONE, base=True)
box('Shaft_Core', (2 * H, 2 * H, 48.0), loc=(0, 0, 2.5), material=RECESS, base=True)
for sx in (-1, 1):
for sy in (-1, 1):
b = box('Corner_Buttress', (1.5, 1.5, 47.6), loc=(sx * (HB - 0.75), sy * (HB - 0.75), 2.85), material=STONE, base=True)
# ── 탑신 한 면: 수직 패널 5열 × 층별 뾰족아치, 가운데 3열에 창 ─────────
parts = []
X0, X1 = -4.9, 4.9
NCOL = 5
colw = (X1 - X0) / NCOL
RIB = 0.32
YF = -H - 0.3 # 리브 바깥면
DEP = 0.36 # 리브 깊이(코어 속으로 0.06 박힘)
YC = -H - 0.3 + DEP / 2 - 0.0
TIER_Z = [2.85 + i * 5.9 for i in range(9)] # 8층 패널 + 꼭대기
for i in range(NCOL + 1):
x = X0 + i * colw
w = RIB if 0 < i < NCOL else 0.5
parts.append(box('Shaft_Rib', (w, DEP, 47.15), loc=(x, YC, 2.85), material=STONE, base=True))
for zi, z in enumerate(TIER_Z):
parts.append(box('Shaft_StringCourse', (10.5, DEP + 0.1, 0.34), loc=(0, YC - 0.05, z), material=STONE, base=True))
if zi < len(TIER_Z) - 1:
parts.append(box('Shaft_Transom', (10.0, DEP - 0.08, 0.2), loc=(0, YC + 0.04, z + 2.6), material=STONE, base=True))
for zi in range(len(TIER_Z) - 1):
z0, z1 = TIER_Z[zi] + 0.34, TIER_Z[zi + 1]
for c in range(NCOL):
cx = X0 + (c + 0.5) * colw
pw = colw - RIB
# 아치 머리(돌 테두리)
parts.append(arch_spandrel('Panel_Arch', pw, 1.15, 1.3, cx, z1 - 1.3, YC, DEP, STONE))
parts.append(arch_spandrel('Panel_Arch_Low', pw, 0.8, 0.9, cx, z0 + 2.46 - 0.9, YC + 0.04, DEP - 0.08, STONE))
# 가운데 3열: 좁은 쌍창
if 1 <= c <= 3 and zi >= 1:
for dx in (-0.3, 0.3):
parts.append(lancet('Shaft_Window', 0.42, 1.75, 0.35, cx + dx, z1 - 3.15, -H - 0.04, 0.12, WIN))
parts.append(box('Window_Mullion', (0.12, 0.2, 2.2), loc=(cx, -H - 0.08, z1 - 3.2), material=STONE, base=True))
face_group(parts, 'Shaft_Face')
# ── 시계층(50~62m) ───────────────────────────────────────
ZC0, ZC1 = 50.0, 62.0
DZ = 56.2 # 시계판 중심 높이
R = 3.5 # 시계판 반지름(지름 7m)
HC = 6.75 # 시계층 반폭(탑신보다 내민다)
box('Clock_Stage', (2 * HC - 0.2, 2 * HC - 0.2, ZC1 - ZC0), loc=(0, 0, ZC0), material=STONE, base=True)
box('Clock_Stage_Corbel', (2 * HC + 0.2, 2 * HC + 0.2, 0.7), loc=(0, 0, ZC0 - 0.1), material=STONE, base=True)
sq_lathe('Clock_Stage_Corbel_Slope', [(0, ZC0 - 1.6), (HB + 0.05, ZC0 - 1.6), (HC + 0.1, ZC0 - 0.1), (0, ZC0 - 0.1)], STONE)
parts = []
YS = -HC + 0.1 # 시계층 벽면
S = 4.3 # 금박 사각 테두리 반폭
# 사각 안의 원: 네 모서리 스팬드럴(금박)
for qx in (-1, 1):
for qz in (-1, 1):
pts = [(R + 0.05, 0), (S, 0), (S, S), (0, S), (0, R + 0.05)]
for i in range(1, 12):
a = math.pi / 2 * (1 - i / 12)
pts.append(((R + 0.05) * math.cos(a), (R + 0.05) * math.sin(a)))
pts = [(qx * x, qz * z) for x, z in pts]
if qx * qz < 0:
pts = list(reversed(pts))
parts.append(extrude_profile('Dial_Spandrel', pts, 0.2, loc=(0, YS - 0.08, DZ), material=GOLD))
# 스팬드럴 장미창 장식
rx, rz = qx * 3.42, qz * 3.42
parts.append(cylinder('Spandrel_Rosette', r=0.42, h=0.08, loc=(rx, YS - 0.27, DZ + rz), rot=(math.pi / 2, 0, 0), material=IRON, seg=24))
parts.append(torus('Spandrel_Rosette_Ring', R=0.42, r=0.06, loc=(rx, YS - 0.33, DZ + rz), rot=(math.pi / 2, 0, 0), material=GOLD, seg=24, rseg=6))
parts.append(sphere('Spandrel_Boss', r=0.12, loc=(rx, YS - 0.35, DZ + rz), material=GOLD, seg=10, rings=6))
# 돌 사각 테두리
for sz in (-1, 1):
parts.append(box('Dial_Frame_H', (2 * S + 1.0, 0.4, 0.5), loc=(0, YS - 0.1, DZ + sz * (S + 0.25)), material=STONE))
for sx in (-1, 1):
parts.append(box('Dial_Frame_V', (0.5, 0.4, 2 * S + 1.0), loc=(sx * (S + 0.25), YS - 0.1, DZ), material=STONE))
# 테두리 위 작은 박공과 아래 금박 명문 띠
parts.append(box('Inscription_Band', (2 * S, 0.12, 0.55), loc=(0, YS - 0.06, DZ - S - 1.05), material=GOLD))
parts.append(box('Inscription_Frame', (2 * S + 1.0, 0.25, 0.25), loc=(0, YS - 0.05, DZ - S - 1.55), material=STONE))
# 시계판 유리
YD = YS - 0.05
parts.append(cylinder('Dial_Glass', r=R, h=0.25, loc=(0, YD + 0.2, DZ), rot=(math.pi / 2, 0, 0), material=DIAL, seg=96))
YD -= 0.05 # 유리 앞면
# 철골: 고리
for rr, tube in ((R, 0.11), (3.15, 0.05), (2.5, 0.06), (1.55, 0.05), (0.6, 0.05)):
parts.append(torus('Dial_Ring', R=rr, r=tube, loc=(0, YD, DZ), rot=(math.pi / 2, 0, 0), material=IRON, seg=96, rseg=6))
# 분 눈금
for m in range(60):
a = 2 * math.pi * m / 60
L = 0.3 if m % 5 == 0 else 0.18
rc = 3.15 + 0.05 + L / 2
parts.append(box('Dial_Minute', (0.05 if m % 5 else 0.09, 0.06, L), loc=(rc * math.sin(a), YD, DZ + rc * math.cos(a)), rot=(0, a, 0), material=IRON))
# 방사형 살(안쪽 장식 트레이서리)
for k in range(24):
a = 2 * math.pi * k / 24
r0, r1 = (0.6, 2.5) if k % 2 == 0 else (1.55, 2.5)
rc, L = (r0 + r1) / 2, r1 - r0
parts.append(box('Dial_Spoke', (0.06, 0.06, L), loc=(rc * math.sin(a), YD, DZ + rc * math.cos(a)), rot=(0, a, 0), material=IRON))
# 로마 숫자(바깥을 향해 방사형으로)
ROMAN = ['XII', 'I', 'II', 'III', 'IIII', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI']
for h, s in enumerate(ROMAN):
a = 2 * math.pi * h / 12
rc = 2.83
t = text_mesh('Dial_Numeral_' + s, s, 0.6, 0.025, IRON)
t.location = (rc * math.sin(a), YD - 0.02, DZ + rc * math.cos(a))
t.rotation_euler = (math.pi / 2, a, 0)
parts.append(t)
# 바늘(10시 10분): 평판 창 모양을 돌출
def hand(name, length, tail, w, angle, y):
pts = [(0, -tail), (w * 0.9, -tail + 0.15), (w * 0.5, 0), (w, length * 0.7), (w * 0.35, length * 0.85),
(0, length), (-w * 0.35, length * 0.85), (-w, length * 0.7), (-w * 0.5, 0), (-w * 0.9, -tail + 0.15)]
pts = list(reversed(pts))
return extrude_profile(name, pts, 0.07, loc=(0, y, DZ), rot=(0, angle, 0), material=IRON)
parts.append(hand('Hour_Hand', 2.2, 0.45, 0.17, 2 * math.pi * (10 + 10 / 60) / 12, YD - 0.12))
parts.append(hand('Minute_Hand', 3.1, 0.8, 0.12, 2 * math.pi * 10 / 60, YD - 0.21))
parts.append(cylinder('Dial_Hub', r=0.22, h=0.32, loc=(0, YD + 0.02, DZ), rot=(math.pi / 2, 0, 0), material=GOLD, seg=24))
# 시계층 위·아래 장식 아치 띠
for c in range(7):
cx = -4.8 + c * 1.6
parts.append(arch_spandrel('Clock_Upper_Arcade', 1.3, 0.6, 0.85, cx, ZC1 - 1.25, YS - 0.1, 0.3, STONE))
face_group(parts, 'Clock_Face')
# 시계층 모서리 첨탑
pin = cylinder('Clock_Corner_Pinnacle', r=0.75, h=ZC1 + 2.6 - ZC0, loc=(HC - 0.5, HC - 0.5, ZC0), material=STONE, seg=8)
pin_cap = cone('Clock_Corner_Pinnacle_Spire', r=0.82, h=3.4, loc=(HC - 0.5, HC - 0.5, ZC1 + 2.6), material=ROOF, seg=8)
pin_tip = sphere('Clock_Corner_Pinnacle_Orb', r=0.22, loc=(HC - 0.5, HC - 0.5, ZC1 + 6.05), material=GOLD, seg=12, rings=8)
for ob in (pin, pin_cap, pin_tip):
for k in (1, 2, 3):
a = k * math.pi / 2
x, y = ob.location.x, ob.location.y
duplicate(ob, loc=(x * math.cos(a) - y * math.sin(a), x * math.sin(a) + y * math.cos(a), ob.location.z))
# ── 코니스와 난간 ───────────────────────────────────────
box('Clock_Cornice', (2 * HC + 0.5, 2 * HC + 0.5, 0.6), loc=(0, 0, ZC1), material=STONE, base=True)
parts = []
for i in range(17):
x = -5.6 + i * 0.7
parts.append(box('Parapet_Post', (0.16, 0.2, 1.0), loc=(x, -HC - 0.05, ZC1 + 0.6), material=GOLD, base=True))
parts.append(box('Parapet_Rail', (11.6, 0.26, 0.18), loc=(0, -HC - 0.05, ZC1 + 1.6), material=GOLD, base=True))
face_group(parts, 'Parapet')
# ── 종루(62.6~73m) ──────────────────────────────────────
ZB0, ZB1 = ZC1 + 0.6, 73.0
HBF = 5.5
box('Belfry_Core', (2 * HBF, 2 * HBF, ZB1 - ZB0), loc=(0, 0, ZB0), material=DARK, base=True)
parts = []
YB = -HBF - 0.2
NB = 5
span = 2 * (HBF - 0.9)
pier = 0.42
ow = (span - (NB - 1) * pier) / NB
OZ0, OZ1 = ZB0 + 1.6, 70.6
parts.append(box('Belfry_Sill', (2 * HBF + 0.3, 0.5, OZ0 - ZB0), loc=(0, YB, ZB0), material=STONE, base=True))
for sx in (-1, 1):
parts.append(box('Belfry_Corner_Pier', (0.95, 0.5, ZB1 - ZB0), loc=(sx * (HBF - 0.475 + 0.1), YB, ZB0), material=STONE, base=True))
for i in range(NB):
cx = -span / 2 + ow / 2 + i * (ow + pier)
if i < NB - 1:
parts.append(box('Belfry_Pier', (pier, 0.5, OZ1 - OZ0), loc=(cx + ow / 2 + pier / 2, YB, OZ0), material=STONE, base=True))
parts.append(arch_spandrel('Belfry_Arch', ow, 1.1, 1.4, cx, OZ1 - 1.4, YB, 0.5, STONE))
# 루버
for j in range(7):
parts.append(box('Belfry_Louvre', (ow - 0.02, 0.06, 0.12), loc=(cx, -HBF - 0.02, OZ0 + 0.5 + j * 0.62), rot=(0.6, 0, 0), material=ROOF))
parts.append(box('Belfry_Frieze', (2 * HBF + 0.3, 0.5, ZB1 - OZ1), loc=(0, YB, OZ1), material=STONE, base=True))
for i in range(12):
parts.append(box('Belfry_Frieze_Shield', (0.36, 0.08, 0.5), loc=(-4.4 + i * 0.8, YB - 0.28, OZ1 + 0.95), material=GOLD))
face_group(parts, 'Belfry_Face')
box('Belfry_Cornice', (2 * HBF + 1.0, 2 * HBF + 1.0, 0.5), loc=(0, 0, ZB1), material=STONE, base=True)
box('Belfry_Crest', (2 * HBF + 0.7, 2 * HBF + 0.7, 0.35), loc=(0, 0, ZB1 + 0.5), material=GOLD, base=True)
# 종루 모서리 소첨탑
bp = cylinder('Belfry_Pinnacle', r=0.42, h=1.6, loc=(HBF + 0.1, HBF + 0.1, ZB1 + 0.85), material=STONE, seg=8)
bc = cone('Belfry_Pinnacle_Spire', r=0.48, h=2.4, loc=(HBF + 0.1, HBF + 0.1, ZB1 + 2.45), material=GOLD, seg=8)
for ob in (bp, bc):
for k in (1, 2, 3):
a = k * math.pi / 2
x, y = ob.location.x, ob.location.y
duplicate(ob, loc=(x * math.cos(a) - y * math.sin(a), x * math.sin(a) + y * math.cos(a), ob.location.z))
# ── 주철 지붕(사각뿔) ───────────────────────────────────
ZR0, ZR1 = ZB1 + 0.85, 87.6
RW0, RW1 = 5.35, 1.35
def roof_half(z):
t = (z - ZR0) / (ZR1 - ZR0)
return RW0 + (RW1 - RW0) * t
sq_lathe('Roof_Pyramid', [(0.0, ZR0), (RW0, ZR0), (RW1, ZR1), (0.0, ZR1)], ROOF)
parts = []
# 도머창(아래 3개, 위 1개)
for zc, xs, sc in ((ZR0 + 2.2, (-2.6, 0, 2.6), 1.0), (ZR0 + 6.4, (-1.2, 1.2), 0.75), (ZR0 + 10.0, (0,), 0.6)):
hz = roof_half(zc)
for x in xs:
dw, dh = 1.1 * sc, 1.6 * sc
y = -hz - 0.25
parts.append(box('Dormer_Body', (dw, 1.6, dh), loc=(x, y + 0.8, zc - dh / 2), material=ROOF, base=True))
parts.append(lancet('Dormer_Window', dw * 0.55, dh * 0.55, 0.25 * sc, x, zc - dh / 2 + 0.2, y - 0.02, 0.05, LAMP))
parts.append(extrude_profile('Dormer_Gable', [(-dw / 2 - 0.12, 0), (dw / 2 + 0.12, 0), (0, 0.9 * sc)], 1.7,
loc=(x, y + 0.82, zc + dh / 2), material=GOLD))
parts.append(cone('Dormer_Finial', r=0.06, h=0.5 * sc, loc=(x, y - 0.02, zc + dh / 2 + 0.85 * sc), material=GOLD, seg=6))
face_group(parts, 'Roof_Face')
# 용마루(모서리) 금박 크로켓
parts = []
NCR = 11
for i in range(NCR):
t = (i + 0.5) / NCR
z = ZR0 + (ZR1 - ZR0) * t
hw = roof_half(z) + 0.02
parts.append(cone('Hip_Crocket', r=0.17, h=0.45, loc=(hw, -hw, z), material=GOLD, seg=6))
hip = curve_tube('Hip_Rib', [(RW0, -RW0, ZR0), (RW1, -RW1, ZR1)], radius=0.09, material=GOLD, resolution=1, bevel_res=1)
face_group(parts, 'Roof_Hip_Crockets')
for k in (1, 2, 3):
duplicate(hip, loc=(0, 0, 0), rot=(0, 0, k * math.pi / 2), name='Hip_Rib')
# ── 에어턴 라이트 랜턴(86~89.6m) ─────────────────────────
ZL0, ZL1 = ZR1, 90.4
HL = 1.35
box('Lantern_Base', (2 * HL + 0.5, 2 * HL + 0.5, 0.4), loc=(0, 0, ZL0), material=GOLD, base=True)
box('Lantern_Light', (2 * HL - 0.3, 2 * HL - 0.3, ZL1 - ZL0 - 0.4), loc=(0, 0, ZL0 + 0.4), material=LAMP, base=True)
parts = []
for sx in (-1, 0, 1):
w = 0.42 if sx else 0.2
parts.append(box('Lantern_Post', (w, 0.3, ZL1 - ZL0 - 0.4), loc=(sx * (HL - w / 2) if sx else 0, -HL + 0.1, ZL0 + 0.4), material=ROOF, base=True))
for sx in (-1, 1):
parts.append(arch_spandrel('Lantern_Arch', HL - 0.31, 0.6, 0.8, sx * (HL / 2 - 0.05), ZL1 - 0.8, -HL + 0.1, 0.3, ROOF))
face_group(parts, 'Lantern_Face')
box('Lantern_Cornice', (2 * HL + 0.5, 2 * HL + 0.5, 0.35), loc=(0, 0, ZL1), material=GOLD, base=True)
# ── 첨탑과 피니얼 ───────────────────────────────────────
ZS0, ZS1 = ZL1 + 0.35, 95.2
sq_lathe('Spire', [(0.0, ZS0), (1.25, ZS0), (0.6, ZS0 + 1.2), (0.06, ZS1), (0.0, ZS1)], ROOF)
for zt, hw in ((ZS0 + 1.2, 0.63), (ZS0 + 2.5, 0.42)):
box('Spire_Gold_Band', (2 * hw, 2 * hw, 0.18), loc=(0, 0, zt), material=GOLD, base=True)
cylinder('Finial_Rod', r=0.06, h=1.6, loc=(0, 0, ZS1 - 0.3), material=GOLD, seg=8)
sphere('Finial_Orb', r=0.24, loc=(0, 0, ZS1 + 0.25), material=GOLD, seg=16, rings=10)
box('Finial_Cross_Bar', (0.7, 0.08, 0.08), loc=(0, 0, ZS1 + 0.95), material=GOLD)
box('Finial_Cross_Bar', (0.08, 0.7, 0.08), loc=(0, 0, ZS1 + 0.95), material=GOLD)

다른 모형