File size: 12,920 Bytes
6b0231d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
"""์žฌ์งˆ๋ณ„๋กœ ํ•ฉ์ณ์ง„ ์ฐจ๋Ÿ‰ glTF ์—์„œ ๋ฐ”ํ€ด๋ฅผ ์‚ผ๊ฐํ˜• ๋‹จ์œ„๋กœ ์ž˜๋ผ ๋‚ด ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๊ด€๋ก€์˜ .glb ๋กœ ๋งŒ๋“ ๋‹ค.

normalize_vehicle_gltf.py ๋Š” ๋ฐ”ํ€ด ๋ถ€ํ’ˆ์ด ๋‚ฑ๊ฐœ ๋…ธ๋“œ๋กœ ์žˆ๋Š” ๋ชจ๋ธ(3ds Max ๋‚ด๋ณด๋‚ด๊ธฐ ๋“ฑ)์„ ๋ฌถ๋Š”๋‹ค. OBJ ์—์„œ ๋ณ€ํ™˜๋œ
Sketchfab ๋ชจ๋ธ(์˜ˆ: Genesis GV80 2021)์€ ๋…ธ๋“œ๊ฐ€ 'Object_N' ์ด๊ณ  ํ•œ ๋ฉ”์‹œ์— ๋„ค ํƒ€์ด์–ด์™€ ์ฐจ์ฒด ์กฐ๊ฐ์ด ์„ž์—ฌ ์žˆ์–ด ๋…ธ๋“œ๋กœ๋Š”
๋ฐ”ํ€ด๋ฅผ ๊ณ ๋ฅผ ์ˆ˜ ์—†๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋ฐ”ํ€ด ์›ํ†ต(์ถ• = ์ขŒ์šฐ, ์ค‘์‹ฌ = ์ฐจ์ถ• ์œ„์น˜, ๋ฐ˜์ง€๋ฆ„ r, ๋ฐ”๊นฅ์ชฝ |X| > inner) ์•ˆ์— ๋ฌด๊ฒŒ์ค‘์‹ฌ์ด
๋“  ์‚ผ๊ฐํ˜•์„ ๋–ผ์–ด ๋ฐ”ํ€ด๋งˆ๋‹ค 'Wheel<k>' ๋…ธ๋“œ ์•„๋ž˜ ์ƒˆ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋กœ ์˜ฎ๊ธด๋‹ค (์ •์  ๋ฒ„ํผ๋Š” ๊ทธ๋Œ€๋กœ ํ•จ๊ป˜ ์“ด๋‹ค).

--repaint: ์žฌ์งˆ ์ด๋ฆ„์ด ์‹ค์ œ ๋ถ€์œ„์™€ ๋’ค์„ž์ธ ๋ชจ๋ธ(GV80 ์›๋ณธ์€ Sketchfab ์—์„œ๋„ ํ›„๋“œ๊ฐ€ ๋…ธ๋ž‘, ๋ฒ”ํผ๊ฐ€ ๋นจ๊ฐ•)์€ ์žฌ์งˆ์„ ๋ฒ„๋ฆฌ๊ณ 
์‚ผ๊ฐํ˜• ์œ„์น˜ยท๋ฒ•์„ ์œผ๋กœ ๋‹ค์‹œ ์น ํ•œ๋‹ค โ€” ํƒ€์ด์–ด ยท ํœ , ์œ ๋ฆฌ(๋ฒจํŠธ๋ผ์ธ ์œ„ ๊ฒฝ์‚ฌ๋ฉด), ๊ทธ๋ฆด, ํ—ค๋“œ๋žจํ”„ ยท ํ…Œ์ผ๋žจํ”„, ํ•˜๋ถ€ ํŠธ๋ฆผ, ๋‚˜๋จธ์ง€ ์ฐจ์ฒด ๋„์žฅ.
๊ตฌ์—ญ ๊ฒฝ๊ณ„๋Š” ์•„๋ž˜ REPAINT ์˜ ๊ฐ’ (๋ฐ”๋‹ฅ ยท ์•ž๋’ค ๋ ๊ธฐ์ค€ ๋ฏธํ„ฐ).

๊ฒฐ๊ณผ ๊ด€๋ก€ (normalize_vehicle_gltf.py ์™€ ๊ฐ™๋‹ค): +Y ์œ„, +Z ์•ž, +X ์™ผ์ชฝ, ๋ฐ”๋‹ฅ(ํƒ€์ด์–ด ์•„๋ž˜) y = 0, ๋ฏธํ„ฐ, ์ˆ˜ํ‰ ์ค‘์‹ฌ = ์ฐจ์ฒด ๊ฒฝ๊ณ„ ์ƒ์ž ์ค‘์•™.

    python scripts/split_wheels_gltf.py assets/gltf/genesis_gv80/scene.gltf assets/gltf/GenesisGV80.glb \\
        --axes x,-z,y --scale 0.01 --axles 1.574,-1.376 --axle-height 0.395 --radius 0.41 --inner 0.60 --repaint
"""

from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

import numpy as np

sys.path.insert(0, str(Path(__file__).resolve().parent))
from normalize_vehicle_gltf import pack_glb, positions  # noqa: E402

_CT = {5121: np.uint8, 5123: np.uint16, 5125: np.uint32}

# --repaint ์žฌ์งˆ (glTF PBR) ๊ณผ ๊ตฌ์—ญ. ๋†’์ด y ๋Š” ๋ฐ”๋‹ฅ์—์„œ, front/rear ๋Š” ์ฐจ ์•ž๋ ยท ๋’ค๋์—์„œ ์•ˆ์ชฝ์œผ๋กœ ์žฐ ๋ฏธํ„ฐ.
MATERIALS = {
    "paint": {"baseColorFactor": [0.86, 0.87, 0.88, 1], "metallicFactor": 0.35, "roughnessFactor": 0.25},  # ํฐ ํŽ„
    "glass": {"baseColorFactor": [0.02, 0.025, 0.03, 1], "metallicFactor": 0.0, "roughnessFactor": 0.05},
    "trim": {"baseColorFactor": [0.03, 0.03, 0.03, 1], "metallicFactor": 0.0, "roughnessFactor": 0.6},
    "grille": {"baseColorFactor": [0.015, 0.015, 0.015, 1], "metallicFactor": 0.6, "roughnessFactor": 0.3},
    "headlight": {"baseColorFactor": [0.9, 0.92, 0.95, 1], "metallicFactor": 0.0, "roughnessFactor": 0.1,
                  "emissive": [0.6, 0.62, 0.65]},
    "taillight": {"baseColorFactor": [0.55, 0.02, 0.02, 1], "metallicFactor": 0.0, "roughnessFactor": 0.15,
                  "emissive": [0.35, 0.0, 0.0]},
    "tire": {"baseColorFactor": [0.025, 0.025, 0.025, 1], "metallicFactor": 0.0, "roughnessFactor": 0.85},
    "rim": {"baseColorFactor": [0.6, 0.6, 0.62, 1], "metallicFactor": 1.0, "roughnessFactor": 0.3},
}
REPAINT = {
    "belt_y": 1.02,         # ์ด ๋†’์ด ์œ„์˜ ๊ธฐ์šด ๋ฉด = ์œ ๋ฆฌ, ๊ฑฐ์˜ ์ˆ˜ํ‰์ธ ๋ฉด(์ง€๋ถ• ยท ํ›„๋“œ ๋) = ๋„์žฅ
    "roof_ny": 0.9,         # |๋ฒ•์„  y| ๊ฐ€ ์ด๋ณด๋‹ค ํฌ๋ฉด ์ˆ˜ํ‰ ๋ฉด
    "trim_y": 0.30,         # ์ด ์•„๋ž˜ = ํ•˜๋ถ€ ํŠธ๋ฆผ (์‚ฌ์ด๋“œ์‹ค ยท ๋ฒ”ํผ ์•„๋ž˜)
    "rim_r": 0.27,          # ๋ฐ”ํ€ด ์›ํ†ต ์•ˆ์—์„œ ์ถ•์œผ๋กœ๋ถ€ํ„ฐ ์ด ๋ฐ˜์ง€๋ฆ„ ์•ˆ = ํœ , ๋ฐ– = ํƒ€์ด์–ด
    "grille": {"front": 0.25, "half_x": 0.50, "y": [0.32, 0.85]},
    "headlight": {"front": 0.35, "min_x": 0.55, "y": [0.72, 1.00]},
    "taillight": {"rear": 0.25, "min_x": 0.45, "y": [0.86, 1.06]},
}


def classify(c: np.ndarray, n: np.ndarray, zmin: float, zmax: float) -> np.ndarray:
    """์ฐจ์ฒด ์‚ผ๊ฐํ˜•(๋ฌด๊ฒŒ์ค‘์‹ฌ c, ๋‹จ์œ„ ๋ฒ•์„  n, ๋ฐ”๋‹ฅ y=0 ๊ธฐ์ค€)์˜ MATERIALS ์ด๋ฆ„."""
    R = REPAINT
    y, ax, z = c[:, 1], np.abs(c[:, 0]), c[:, 2]
    out = np.full(len(c), "paint", dtype=object)
    out[y < R["trim_y"]] = "trim"
    out[(y > R["belt_y"]) & (np.abs(n[:, 1]) < R["roof_ny"])] = "glass"
    g, h, t = R["grille"], R["headlight"], R["taillight"]
    out[(z > zmax - g["front"]) & (ax < g["half_x"]) & (y > g["y"][0]) & (y < g["y"][1])] = "grille"
    out[(z > zmax - h["front"]) & (ax > h["min_x"]) & (y > h["y"][0]) & (y < h["y"][1])] = "headlight"
    out[(z < zmin + t["rear"]) & (ax > t["min_x"]) & (y > t["y"][0]) & (y < t["y"][1])] = "taillight"
    return out


def read_indices(j: dict, blob: bytes, acc: int, nverts: int) -> np.ndarray:
    if acc is None:
        return np.arange(nverts, dtype=np.uint32)
    a = j["accessors"][acc]
    bv = j["bufferViews"][a["bufferView"]]
    dt = _CT[a["componentType"]]
    off = bv.get("byteOffset", 0) + a.get("byteOffset", 0)
    return np.frombuffer(blob, dtype=dt, count=a["count"], offset=off).astype(np.uint32)


def axes_matrix(spec: str) -> np.ndarray:
    """'x,-z,y' โ†’ ์›๋ณธ ์ขŒํ‘œ์—์„œ (์‹œ๋ฎฌ X, Y, Z) ๋ฅผ ๊ณ ๋ฅด๋Š” 3x3."""
    R = np.zeros((3, 3))
    for row, s in enumerate(spec.split(",")):
        s = s.strip()
        R[row, "xyz".index(s[-1])] = -1.0 if s.startswith("-") else 1.0
    if abs(np.linalg.det(R) - 1) > 1e-6:
        raise SystemExit(f"--axes {spec} ๋Š” ํšŒ์ „์ด ์•„๋‹ˆ๋‹ค (det={np.linalg.det(R):.0f}) โ€” ์ขŒ์šฐ๊ฐ€ ๋’ค์ง‘ํžŒ๋‹ค")
    return R


def main() -> None:
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("src", type=Path, help="์›๋ณธ .gltf (๊ฐ™์€ ํด๋”์— .bin)")
    ap.add_argument("out", type=Path, help="๊ฒฐ๊ณผ .glb")
    ap.add_argument("--axes", default="x,y,z", help="์‹œ๋ฎฌ X(์™ผ์ชฝ),Y(์œ„),Z(์•ž) ๊ฐ€ ์›๋ณธ์˜ ์–ด๋А ์ถ•์ธ์ง€, ์˜ˆ: x,-z,y")
    ap.add_argument("--scale", type=float, default=1.0, help="์›๋ณธ ๋‹จ์œ„ โ†’ ๋ฏธํ„ฐ")
    ap.add_argument("--axles", required=True, help="์•ž ์ฐจ์ถ• Z, ๋’ค ์ฐจ์ถ• Z (์ถ•ยท๋ฐฐ์œจ ์ ์šฉ ํ›„, ์ค‘์‹ฌ ๋งž์ถ”๊ธฐ ์ „, ๋ฏธํ„ฐ)")
    ap.add_argument("--axle-height", type=float, required=True, help="์ฐจ์ถ• ๋†’์ด Y (์ถ•ยท๋ฐฐ์œจ ์ ์šฉ ํ›„, ๋ฏธํ„ฐ)")
    ap.add_argument("--radius", type=float, required=True, help="๋ฐ”ํ€ด ์›ํ†ต ๋ฐ˜์ง€๋ฆ„ (ํƒ€์ด์–ด ๋ฐ”๊นฅ + ์—ฌ์œ , ๋ฏธํ„ฐ)")
    ap.add_argument("--inner", type=float, required=True, help="๋ฐ”ํ€ด๋กœ ๋ณผ |X| ์ตœ์†Œ (๋ฏธํ„ฐ, ๋ฐ”ํ€ด ์•ˆ์ชฝ ๋ฉด)")
    ap.add_argument("--repaint", action="store_true", help="์›๋ณธ ์žฌ์งˆ์„ ๋ฒ„๋ฆฌ๊ณ  ์œ„์น˜๋กœ ๋‹ค์‹œ ์น ํ•œ๋‹ค (REPAINT)")
    a = ap.parse_args()

    j = json.loads(a.src.read_text())
    blob = bytearray((a.src.parent / j["buffers"][0]["uri"]).read_bytes())
    R = axes_matrix(a.axes) * a.scale
    zf, zr = (float(v) for v in a.axles.split(","))
    centers = [(+1, zf), (-1, zf), (+1, zr), (-1, zr)]  # (X ๋ถ€ํ˜ธ, Z): ์•ž์™ผ, ์•ž์˜ค, ๋’ค์™ผ, ๋’ค์˜ค
    names = ["WheelFrontLeft", "WheelFrontRight", "WheelRearLeft", "WheelRearRight"]

    mesh_nodes = [i for i, n in enumerate(j["nodes"]) if "mesh" in n]
    for i in mesh_nodes:
        n = j["nodes"][i]
        if any(k in n for k in ("matrix", "translation", "rotation", "scale")):
            raise SystemExit(f"๋ฉ”์‹œ ๋…ธ๋“œ {n.get('name')} ์— ๋ณ€ํ™˜์ด ์žˆ๋‹ค โ€” ์ด ์Šคํฌ๋ฆฝํŠธ๋Š” ํ•ญ๋“ฑ ๋ณ€ํ™˜์ธ ๋ฉ”์‹œ ๋…ธ๋“œ๋งŒ ๋‹ค๋ฃฌ๋‹ค")
    if len(j["buffers"]) != 1:
        raise SystemExit("๋ฒ„ํผ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ธ glTF ๋Š” ๋‹ค๋ฃจ์ง€ ์•Š๋Š”๋‹ค")

    def add_indices(idx: np.ndarray) -> int:
        while len(blob) % 4:
            blob.append(0)
        data = idx.astype(np.uint32).tobytes()
        j["bufferViews"].append({"buffer": 0, "byteOffset": len(blob), "byteLength": len(data), "target": 34963})
        blob.extend(data)
        j["accessors"].append({"bufferView": len(j["bufferViews"]) - 1, "componentType": 5125,
                               "count": int(idx.size), "type": "SCALAR"})
        return len(j["accessors"]) - 1

    prims = []  # (ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ, ์‹œ๋ฎฌ ์ขŒํ‘œ ์ •์ (์ค‘์‹ฌ ๋งž์ถ”๊ธฐ ์ „), ์‚ผ๊ฐํ˜•)
    lo, hi = np.full(3, np.inf), np.full(3, -np.inf)
    for i in mesh_nodes:
        for p in j["meshes"][j["nodes"][i]["mesh"]]["primitives"]:
            if p.get("mode", 4) != 4:
                continue
            v = positions(j, a.src.parent, p["attributes"]["POSITION"]) @ R.T
            lo, hi = np.minimum(lo, v.min(0)), np.maximum(hi, v.max(0))
            prims.append((p, v, read_indices(j, bytes(blob), p.get("indices"), len(v)).reshape(-1, 3)))

    mat_id = {}
    if a.repaint:
        for name, m in MATERIALS.items():
            pbr = {k: m[k] for k in ("baseColorFactor", "metallicFactor", "roughnessFactor")}
            mm = {"name": f"sim_{name}", "pbrMetallicRoughness": pbr, "doubleSided": True}
            if "emissive" in m:
                mm["emissiveFactor"] = m["emissive"]
            j["materials"].append(mm)
            mat_id[name] = len(j["materials"]) - 1

    def sub(p: dict, tris: np.ndarray, mat: str | None) -> dict:
        q = {kk: vv for kk, vv in p.items() if kk != "indices"}
        q["indices"] = add_indices(tris.reshape(-1))
        if mat is not None:
            q["material"] = mat_id[mat]
        return q

    wheel_prims: list[list[dict]] = [[] for _ in centers]
    n_wheel = n_all = 0
    counts: dict[str, int] = {}
    for p, v, tri in prims:
        c = v[tri].mean(1)
        owner = np.full(len(tri), -1)
        for k, (sx, zc) in enumerate(centers):
            inside = (sx * c[:, 0] > a.inner) & (np.hypot(c[:, 1] - a.axle_height, c[:, 2] - zc) < a.radius)
            owner[inside & (owner < 0)] = k
        n_all += len(tri)
        n_wheel += int((owner >= 0).sum())
        if a.repaint:
            e = np.cross(v[tri[:, 1]] - v[tri[:, 0]], v[tri[:, 2]] - v[tri[:, 0]])
            nrm = e / np.maximum(np.linalg.norm(e, axis=1, keepdims=True), 1e-12)
            cls = classify(c - [0, lo[1], 0], nrm, lo[2], hi[2])
            for k, (_, zc) in enumerate(centers):
                w = owner == k
                cls[w] = np.where(np.hypot(c[w, 1] - a.axle_height, c[w, 2] - zc) < REPAINT["rim_r"], "rim", "tire")
        new_body = []
        for k in range(len(centers)):
            sel = owner == k
            if not sel.any():
                continue
            if a.repaint:
                for m in ("tire", "rim"):
                    s2 = sel & (cls == m)
                    if s2.any():
                        wheel_prims[k].append(sub(p, tri[s2], m))
                        counts[m] = counts.get(m, 0) + int(s2.sum())
            else:
                wheel_prims[k].append(sub(p, tri[sel], None))
        body = owner < 0
        if a.repaint:
            for m in MATERIALS:
                s2 = body & (cls == m)
                if s2.any():
                    new_body.append(sub(p, tri[s2], m))
                    counts[m] = counts.get(m, 0) + int(s2.sum())
        elif body.any():
            new_body.append(sub(p, tri[body], None))
        p["_split"] = new_body
    # ์ฐจ์ฒด ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋ฅผ ์žฌ์งˆ๋ณ„ ์กฐ๊ฐ์œผ๋กœ ๋ฐ”๊พผ๋‹ค
    for i in mesh_nodes:
        mesh = j["meshes"][j["nodes"][i]["mesh"]]
        out = []
        for p in mesh["primitives"]:
            out += p.pop("_split") if "_split" in p else [p]
        mesh["primitives"] = [q for q in out if j["accessors"][q["indices"]]["count"] > 0]
    if a.repaint:
        print("๋‹ค์‹œ ์น ํ•œ ์‚ผ๊ฐํ˜•:", ", ".join(f"{m} {n:,}" for m, n in counts.items()))

    root_children = list(mesh_nodes)
    for k, prims in enumerate(wheel_prims):
        if not prims:
            raise SystemExit(f"{names[k]} ์— ์‚ผ๊ฐํ˜•์ด ์—†๋‹ค โ€” --axles/--axle-height/--radius/--inner ํ™•์ธ")
        j["meshes"].append({"name": names[k], "primitives": prims})
        j["nodes"].append({"name": names[k], "children": [len(j["nodes"]) + 1]})
        j["nodes"].append({"name": names[k] + "_mesh", "mesh": len(j["meshes"]) - 1})
        root_children.append(len(j["nodes"]) - 2)
    # ๋ฉ”์‹œ ๋…ธ๋“œ๋Š” ์›๋ž˜ ๋ถ€๋ชจ์—์„œ ๋–ผ๊ณ  ์ •๋ ฌ ๋…ธ๋“œ ํ•˜๋‚˜ ์•„๋ž˜๋กœ (์›๋ณธ ๋ฃจํŠธ์˜ ์ถ• ํšŒ์ „์€ ๋ฒ„๋ฆฌ๊ณ  --axes ๋กœ ๋Œ€์‹ ํ•œ๋‹ค)
    for n in j["nodes"]:
        if "children" in n and not n.get("name", "").startswith("Wheel"):
            n["children"] = [c for c in n["children"] if c not in mesh_nodes]
    M = np.eye(4)
    M[:3, :3] = R
    M[:3, 3] = [-(lo[0] + hi[0]) / 2, -lo[1], -(lo[2] + hi[2]) / 2]
    j["nodes"].append({"name": "SimAlign", "matrix": M.T.reshape(-1).tolist(), "children": root_children})
    j["scenes"] = [{"nodes": [len(j["nodes"]) - 1]}]
    j["scene"] = 0
    size = hi - lo
    print(f"ํฌ๊ธฐ ํญ {size[0]:.3f} x ๋†’์ด {size[1]:.3f} x ๊ธธ์ด {size[2]:.3f} m, ์ถ•๊ฑฐ {zf - zr:.3f} m, "
          f"๋ฐ”ํ€ด ์‚ผ๊ฐํ˜• {n_wheel:,} / {n_all:,} ({[sum(int(j['accessors'][q['indices']]['count']) // 3 for q in w) for w in wheel_prims]})")

    tmp = a.out.with_suffix(".bin.tmp")
    tmp.write_bytes(bytes(blob))
    j["buffers"] = [{"uri": tmp.name, "byteLength": len(blob)}]
    a.out.parent.mkdir(parents=True, exist_ok=True)
    try:
        pack_glb(j, tmp.parent, a.out)
    finally:
        tmp.unlink()


if __name__ == "__main__":
    main()