From 1487026b8ed2bcee2716e26f5ed0b12698de5569 Mon Sep 17 00:00:00 2001 From: Asti1982 Date: Fri, 12 Jun 2026 23:38:35 +0200 Subject: [PATCH] props: add RustChain vintage PowerPC tower --- .../rustchain_vintage_powerpc_tower/LICENSE | 14 + .../rustchain_vintage_powerpc_tower/README.md | 53 + .../preview.png | Bin 0 -> 53360 bytes .../rustchain_vintage_powerpc_tower.iqm | Bin 0 -> 12840 bytes .../rustchain_vintage_powerpc_tower.mtl | 28 + .../rustchain_vintage_powerpc_tower.obj | 1092 +++++++++++++++++ .../rustchain_vintage_powerpc_tower.skin | 4 + .../textures/powerpc_case.tga | Bin 0 -> 196652 bytes .../textures/powerpc_dark.tga | Bin 0 -> 196652 bytes .../textures/powerpc_glow.tga | Bin 0 -> 196652 bytes .../textures/powerpc_label.tga | Bin 0 -> 196652 bytes tests/test_vintage_powerpc_tower_prop.py | 67 + ...te_rustchain_vintage_powerpc_tower_prop.py | 320 +++++ 13 files changed, 1578 insertions(+) create mode 100644 pk3_build/models/props/rustchain_vintage_powerpc_tower/LICENSE create mode 100644 pk3_build/models/props/rustchain_vintage_powerpc_tower/README.md create mode 100644 pk3_build/models/props/rustchain_vintage_powerpc_tower/preview.png create mode 100644 pk3_build/models/props/rustchain_vintage_powerpc_tower/rustchain_vintage_powerpc_tower.iqm create mode 100644 pk3_build/models/props/rustchain_vintage_powerpc_tower/rustchain_vintage_powerpc_tower.mtl create mode 100644 pk3_build/models/props/rustchain_vintage_powerpc_tower/rustchain_vintage_powerpc_tower.obj create mode 100644 pk3_build/models/props/rustchain_vintage_powerpc_tower/rustchain_vintage_powerpc_tower.skin create mode 100644 pk3_build/models/props/rustchain_vintage_powerpc_tower/textures/powerpc_case.tga create mode 100644 pk3_build/models/props/rustchain_vintage_powerpc_tower/textures/powerpc_dark.tga create mode 100644 pk3_build/models/props/rustchain_vintage_powerpc_tower/textures/powerpc_glow.tga create mode 100644 pk3_build/models/props/rustchain_vintage_powerpc_tower/textures/powerpc_label.tga create mode 100644 tests/test_vintage_powerpc_tower_prop.py create mode 100644 tools/generate_rustchain_vintage_powerpc_tower_prop.py diff --git a/pk3_build/models/props/rustchain_vintage_powerpc_tower/LICENSE b/pk3_build/models/props/rustchain_vintage_powerpc_tower/LICENSE new file mode 100644 index 0000000..f7b7682 --- /dev/null +++ b/pk3_build/models/props/rustchain_vintage_powerpc_tower/LICENSE @@ -0,0 +1,14 @@ +RustChain Vintage PowerPC Tower prop + +Copyright 2026 + +This model, texture pack, preview image, and deterministic procedural source +are licensed under the Creative Commons Attribution-ShareAlike 4.0 +International License (CC-BY-SA-4.0). + +You are free to share and adapt the material for any purpose, even +commercially, provided attribution is given and adaptations are distributed +under the same license. + +License summary and legal code: +https://creativecommons.org/licenses/by-sa/4.0/ diff --git a/pk3_build/models/props/rustchain_vintage_powerpc_tower/README.md b/pk3_build/models/props/rustchain_vintage_powerpc_tower/README.md new file mode 100644 index 0000000..9c1e3f4 --- /dev/null +++ b/pk3_build/models/props/rustchain_vintage_powerpc_tower/README.md @@ -0,0 +1,53 @@ +# RustChain Vintage PowerPC Tower prop + +Original low-poly set-dressing prop for the Xonotic RustChain Arena prop bounty #14015: +https://github.com/Scottcjn/rustchain-bounties/issues/14015 + +## Package + +| File | Purpose | +| --- | --- | +| `rustchain_vintage_powerpc_tower.iqm` | Static IQM model for Xonotic/DarkPlaces | +| `rustchain_vintage_powerpc_tower.obj` | Editable source mesh generated by the Python script | +| `rustchain_vintage_powerpc_tower.mtl` | Source material table for the OBJ | +| `rustchain_vintage_powerpc_tower.skin` | Xonotic material-to-texture mapping | +| `textures/*.tga` | 256x256 power-of-two texture set | +| `preview.png` | Generated 1280x720 preview sheet | +| `README.md` | Integration and design notes | +| `LICENSE` | CC-BY-SA-4.0 / GPL-compatible asset license | + +## Design + +This implements the **Vintage PowerPC / retro tower** wishlist item. The prop is +a beige late-1990s tower silhouette with stacked optical/floppy bays, vertical +front vents, a small cyan RTC activity window, colored side badge stripes, rear +port hints, and chunky pedestal feet. It is meant for antiquity-vault corners, +server rooms, spawn alcoves, and map-side storytelling around old silicon. + +The geometry and textures are original procedural assets. No external mesh, +photo texture, AI-generated model file, or trademarked logo is embedded. + +## Regeneration + +From the repository root: + +```powershell +python tools/generate_rustchain_vintage_powerpc_tower_prop.py +``` + +The script writes the OBJ/MTL source, TGA textures, PNG preview, README, +LICENSE, and skin file. The committed IQM was converted from the generated OBJ +with the repository IQM compiler rebuilt locally for the current Linux runtime: + +```bash +cd source/iqm +g++ -O3 -fomit-frame-pointer -Wall -fsigned-char -o /tmp/iqm-local iqm.cpp +cd ../.. +/tmp/iqm-local pk3_build/models/props/rustchain_vintage_powerpc_tower/rustchain_vintage_powerpc_tower.iqm pk3_build/models/props/rustchain_vintage_powerpc_tower/rustchain_vintage_powerpc_tower.obj +``` + +## Placement + +Suggested use: low-cover set dressing against walls, behind glass, or inside an +antiquity vault. The model is static and low-poly; mappers can scale it to fit +server-room corridors or museum-style displays. diff --git a/pk3_build/models/props/rustchain_vintage_powerpc_tower/preview.png b/pk3_build/models/props/rustchain_vintage_powerpc_tower/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..f2323917d33689d6cf3c59b4e1b5055a1cd3d4c5 GIT binary patch literal 53360 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 LUw--h&)@z7C&c-< literal 0 HcmV?d00001 diff --git a/pk3_build/models/props/rustchain_vintage_powerpc_tower/textures/powerpc_dark.tga b/pk3_build/models/props/rustchain_vintage_powerpc_tower/textures/powerpc_dark.tga new file mode 100644 index 0000000000000000000000000000000000000000..e4a899e32100a18af9487b5b7ede9302116ad1a9 GIT binary patch literal 196652 zcmeI%L24F36h_gGt8gIV26Pt@evk+ngZp2J4#WjC4IlJ9A=9M#op)eep|RC2*qHYyV_5xN`~o6}UF$6aUQ?2n+>I6npKT3}<&V zfoo$v@!za~z);{svDf~|aCS!%xHje!|IG>r3y|Kn|t6$90sWV^Z}xfrK7arL z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1nwc=37)`EV19{)vpbrA{xwh*(0_UR zX4k(4$^!Z?Z{O_t*Faf7|K;tQecrzhAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5;&dkA=fComM4U!vjcjwYag4U`4+U*5ji^{;`lfd0$dH@p5dP!`aCdHZIc_wNG; z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAVAy|Kn_d4JC=2MnynVClUjtQh<@4{ak8jUkjz2FXkwpLi literal 0 HcmV?d00001 diff --git a/pk3_build/models/props/rustchain_vintage_powerpc_tower/textures/powerpc_glow.tga b/pk3_build/models/props/rustchain_vintage_powerpc_tower/textures/powerpc_glow.tga new file mode 100644 index 0000000000000000000000000000000000000000..a5a6dc25b49e386eaac055a02eed24d2008c9142 GIT binary patch literal 196652 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 R*s*J8+urRv9_!zj`v*$5ykP(U literal 0 HcmV?d00001 diff --git a/tests/test_vintage_powerpc_tower_prop.py b/tests/test_vintage_powerpc_tower_prop.py new file mode 100644 index 0000000..10eea8b --- /dev/null +++ b/tests/test_vintage_powerpc_tower_prop.py @@ -0,0 +1,67 @@ +import struct +import unittest +from pathlib import Path + +from PIL import Image + + +ROOT = Path(__file__).resolve().parents[1] +PROP_NAME = "rustchain_vintage_powerpc_tower" +PROP = ROOT / "pk3_build" / "models" / "props" / PROP_NAME + + +class VintagePowerPCTowerPropTest(unittest.TestCase): + def test_required_bounty_files_exist(self): + required = [ + f"{PROP_NAME}.iqm", + f"{PROP_NAME}.obj", + f"{PROP_NAME}.mtl", + f"{PROP_NAME}.skin", + "preview.png", + "README.md", + "LICENSE", + "textures/powerpc_case.tga", + "textures/powerpc_dark.tga", + "textures/powerpc_glow.tga", + "textures/powerpc_label.tga", + ] + for name in required: + self.assertTrue((PROP / name).is_file(), name) + + def test_iqm_header_is_static_model(self): + data = (PROP / f"{PROP_NAME}.iqm").read_bytes() + values = struct.unpack("<16s27I", data[:124]) + self.assertEqual(values[0], b"INTERQUAKEMODEL\0") + self.assertEqual(values[1], 2) + self.assertEqual(values[2], len(data)) + self.assertGreaterEqual(values[6], 1) + self.assertGreaterEqual(values[8], 2) + self.assertGreater(values[9], 40) + self.assertGreater(values[11], 20) + self.assertEqual(values[14], 0) + self.assertEqual(values[18], 0) + + def test_textures_and_preview_have_expected_sizes(self): + for texture in (PROP / "textures").glob("*.tga"): + with Image.open(texture) as image: + self.assertEqual(image.size, (256, 256), texture.name) + with Image.open(PROP / "preview.png") as preview: + self.assertEqual(preview.size, (1280, 720)) + self.assertEqual(preview.format, "PNG") + + def test_skin_and_obj_reference_all_materials(self): + skin = (PROP / f"{PROP_NAME}.skin").read_text(encoding="ascii") + obj = (PROP / f"{PROP_NAME}.obj").read_text(encoding="ascii") + for material in ("powerpc_case", "powerpc_dark", "powerpc_glow", "powerpc_label"): + self.assertIn(material, skin) + self.assertIn(f"usemtl {material}", obj) + + def test_readme_identifies_distinct_wishlist_item(self): + readme = (PROP / "README.md").read_text(encoding="utf-8") + self.assertIn("bounty #14015", readme.lower()) + self.assertIn("vintage powerpc", readme.lower()) + self.assertIn("retro tower", readme.lower()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/generate_rustchain_vintage_powerpc_tower_prop.py b/tools/generate_rustchain_vintage_powerpc_tower_prop.py new file mode 100644 index 0000000..46acbcc --- /dev/null +++ b/tools/generate_rustchain_vintage_powerpc_tower_prop.py @@ -0,0 +1,320 @@ +"""Generate the RustChain vintage PowerPC-style tower prop package. + +The output is an original low-poly static prop for bounty #14015: +pk3_build/models/props/rustchain_vintage_powerpc_tower/. +""" + +from __future__ import annotations + +from pathlib import Path + +from PIL import Image, ImageDraw, ImageFont + + +ROOT = Path(__file__).resolve().parents[1] +PROP_NAME = "rustchain_vintage_powerpc_tower" +OUT = ROOT / "pk3_build" / "models" / "props" / PROP_NAME +TEXTURES = OUT / "textures" + +OBJ_FILE = OUT / f"{PROP_NAME}.obj" +MTL_FILE = OUT / f"{PROP_NAME}.mtl" +SKIN_FILE = OUT / f"{PROP_NAME}.skin" +PREVIEW_FILE = OUT / "preview.png" +IQM_FILE = OUT / f"{PROP_NAME}.iqm" + +MATERIALS = { + "powerpc_case": { + "texture": "textures/powerpc_case.tga", + "color": (190, 181, 154), + "kd": (0.72, 0.68, 0.58), + }, + "powerpc_dark": { + "texture": "textures/powerpc_dark.tga", + "color": (34, 36, 38), + "kd": (0.12, 0.13, 0.14), + }, + "powerpc_glow": { + "texture": "textures/powerpc_glow.tga", + "color": (31, 216, 224), + "kd": (0.05, 0.84, 0.88), + }, + "powerpc_label": { + "texture": "textures/powerpc_label.tga", + "color": (220, 152, 52), + "kd": (0.82, 0.50, 0.18), + }, +} + +README_TEXT = """# RustChain Vintage PowerPC Tower prop + +Original low-poly set-dressing prop for the Xonotic RustChain Arena prop bounty #14015: +https://github.com/Scottcjn/rustchain-bounties/issues/14015 + +## Package + +| File | Purpose | +| --- | --- | +| `rustchain_vintage_powerpc_tower.iqm` | Static IQM model for Xonotic/DarkPlaces | +| `rustchain_vintage_powerpc_tower.obj` | Editable source mesh generated by the Python script | +| `rustchain_vintage_powerpc_tower.mtl` | Source material table for the OBJ | +| `rustchain_vintage_powerpc_tower.skin` | Xonotic material-to-texture mapping | +| `textures/*.tga` | 256x256 power-of-two texture set | +| `preview.png` | Generated 1280x720 preview sheet | +| `README.md` | Integration and design notes | +| `LICENSE` | CC-BY-SA-4.0 / GPL-compatible asset license | + +## Design + +This implements the **Vintage PowerPC / retro tower** wishlist item. The prop is +a beige late-1990s tower silhouette with stacked optical/floppy bays, vertical +front vents, a small cyan RTC activity window, colored side badge stripes, rear +port hints, and chunky pedestal feet. It is meant for antiquity-vault corners, +server rooms, spawn alcoves, and map-side storytelling around old silicon. + +The geometry and textures are original procedural assets. No external mesh, +photo texture, AI-generated model file, or trademarked logo is embedded. + +## Regeneration + +From the repository root: + +```powershell +python tools/generate_rustchain_vintage_powerpc_tower_prop.py +``` + +The script writes the OBJ/MTL source, TGA textures, PNG preview, README, +LICENSE, and skin file. The committed IQM was converted from the generated OBJ +with the repository IQM compiler rebuilt locally for the current Linux runtime: + +```bash +cd source/iqm +g++ -O3 -fomit-frame-pointer -Wall -fsigned-char -o /tmp/iqm-local iqm.cpp +cd ../.. +/tmp/iqm-local pk3_build/models/props/rustchain_vintage_powerpc_tower/rustchain_vintage_powerpc_tower.iqm \ + pk3_build/models/props/rustchain_vintage_powerpc_tower/rustchain_vintage_powerpc_tower.obj +``` + +## Placement + +Suggested use: low-cover set dressing against walls, behind glass, or inside an +antiquity vault. The model is static and low-poly; mappers can scale it to fit +server-room corridors or museum-style displays. +""" + +LICENSE_TEXT = """RustChain Vintage PowerPC Tower prop + +Copyright 2026 + +This model, texture pack, preview image, and deterministic procedural source +are licensed under the Creative Commons Attribution-ShareAlike 4.0 +International License (CC-BY-SA-4.0). + +You are free to share and adapt the material for any purpose, even +commercially, provided attribution is given and adaptations are distributed +under the same license. + +License summary and legal code: +https://creativecommons.org/licenses/by-sa/4.0/ +""" + + +class ObjMesh: + def __init__(self) -> None: + self.vertices: list[tuple[float, float, float]] = [] + self.uvs: list[tuple[float, float]] = [] + self.faces: list[tuple[str, tuple[int, int, int, int], tuple[int, int, int, int]]] = [] + + def _add_vertex(self, xyz: tuple[float, float, float], uv: tuple[float, float]) -> tuple[int, int]: + self.vertices.append(xyz) + self.uvs.append(uv) + idx = len(self.vertices) + return idx, idx + + def add_quad(self, material: str, points: list[tuple[float, float, float]]) -> None: + uv_points = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)] + pairs = [self._add_vertex(point, uv) for point, uv in zip(points, uv_points)] + self.faces.append((material, tuple(v for v, _ in pairs), tuple(t for _, t in pairs))) + + def add_box( + self, + center: tuple[float, float, float], + size: tuple[float, float, float], + material: str, + ) -> None: + cx, cy, cz = center + sx, sy, sz = (axis / 2.0 for axis in size) + x0, x1 = cx - sx, cx + sx + y0, y1 = cy - sy, cy + sy + z0, z1 = cz - sz, cz + sz + self.add_quad(material, [(x0, y0, z0), (x1, y0, z0), (x1, y0, z1), (x0, y0, z1)]) + self.add_quad(material, [(x1, y1, z0), (x0, y1, z0), (x0, y1, z1), (x1, y1, z1)]) + self.add_quad(material, [(x0, y1, z0), (x0, y0, z0), (x0, y0, z1), (x0, y1, z1)]) + self.add_quad(material, [(x1, y0, z0), (x1, y1, z0), (x1, y1, z1), (x1, y0, z1)]) + self.add_quad(material, [(x0, y0, z1), (x1, y0, z1), (x1, y1, z1), (x0, y1, z1)]) + self.add_quad(material, [(x0, y1, z0), (x1, y1, z0), (x1, y0, z0), (x0, y0, z0)]) + + def write_obj(self, path: Path) -> None: + lines = [ + f"# {PROP_NAME} - deterministic source mesh", + f"mtllib {MTL_FILE.name}", + "o rustchain_vintage_powerpc_tower", + ] + for vertex in self.vertices: + lines.append("v {:.4f} {:.4f} {:.4f}".format(*vertex)) + for uv in self.uvs: + lines.append("vt {:.4f} {:.4f}".format(*uv)) + current_material = "" + for material, verts, texcoords in self.faces: + if material != current_material: + lines.append(f"usemtl {material}") + current_material = material + refs = [f"{v}/{t}" for v, t in zip(verts, texcoords)] + lines.append("f " + " ".join(refs)) + path.write_text("\n".join(lines) + "\n", encoding="ascii") + + +def font(size: int) -> ImageFont.ImageFont: + for name in ("DejaVuSans.ttf", "arial.ttf"): + try: + return ImageFont.truetype(name, size) + except OSError: + continue + return ImageFont.load_default() + + +def write_texture(name: str, base: tuple[int, int, int], accent: tuple[int, int, int]) -> None: + image = Image.new("RGB", (256, 256), base) + draw = ImageDraw.Draw(image) + if "case" in name: + for x in range(0, 256, 32): + draw.line((x, 0, x, 255), fill=tuple(max(0, c - 18) for c in base)) + for y in range(0, 256, 32): + draw.line((0, y, 255, y), fill=tuple(min(255, c + 12) for c in base)) + draw.rectangle((18, 18, 238, 238), outline=accent, width=3) + elif "dark" in name: + for y in range(12, 256, 18): + draw.rectangle((22, y, 234, y + 7), fill=accent) + elif "glow" in name: + for r in range(110, 8, -12): + shade = tuple(min(255, int(c * (0.28 + r / 130))) for c in accent) + draw.ellipse((128 - r, 128 - r, 128 + r, 128 + r), outline=shade, width=5) + draw.text((86, 110), "RTC", fill=(8, 34, 38), font=font(36)) + else: + colors = [(220, 67, 57), (239, 167, 45), (86, 184, 96), (58, 137, 220)] + for idx, color in enumerate(colors): + draw.rectangle((0, idx * 64, 256, idx * 64 + 47), fill=color) + draw.text((54, 196), "PPC", fill=(30, 28, 24), font=font(36)) + image.save(TEXTURES / name) + + +def write_textures() -> None: + write_texture("powerpc_case.tga", (190, 181, 154), (128, 117, 88)) + write_texture("powerpc_dark.tga", (28, 30, 32), (78, 84, 86)) + write_texture("powerpc_glow.tga", (10, 39, 43), (31, 216, 224)) + write_texture("powerpc_label.tga", (220, 152, 52), (255, 232, 142)) + + +def build_mesh() -> ObjMesh: + mesh = ObjMesh() + mesh.add_box((0.0, 0.0, 1.45), (1.05, 0.54, 2.80), "powerpc_case") + mesh.add_box((0.0, -0.31, 1.45), (0.88, 0.07, 2.42), "powerpc_case") + mesh.add_box((0.0, -0.36, 2.42), (0.62, 0.06, 0.18), "powerpc_dark") + mesh.add_box((0.0, -0.36, 2.12), (0.62, 0.06, 0.13), "powerpc_dark") + mesh.add_box((0.0, -0.365, 1.78), (0.42, 0.05, 0.11), "powerpc_dark") + mesh.add_box((-0.28, -0.37, 1.34), (0.10, 0.05, 0.84), "powerpc_dark") + mesh.add_box((-0.11, -0.37, 1.34), (0.10, 0.05, 0.84), "powerpc_dark") + mesh.add_box((0.06, -0.37, 1.34), (0.10, 0.05, 0.84), "powerpc_dark") + mesh.add_box((0.29, -0.37, 1.43), (0.20, 0.05, 0.26), "powerpc_glow") + mesh.add_box((0.26, -0.37, 0.94), (0.34, 0.05, 0.12), "powerpc_label") + mesh.add_box((-0.42, -0.39, 0.20), (0.23, 0.20, 0.16), "powerpc_case") + mesh.add_box((0.42, -0.39, 0.20), (0.23, 0.20, 0.16), "powerpc_case") + mesh.add_box((-0.56, 0.0, 1.45), (0.07, 0.48, 2.46), "powerpc_dark") + mesh.add_box((0.56, 0.0, 1.45), (0.07, 0.48, 2.46), "powerpc_dark") + mesh.add_box((0.0, 0.31, 1.68), (0.72, 0.07, 1.50), "powerpc_dark") + mesh.add_box((-0.26, 0.36, 1.98), (0.14, 0.05, 0.12), "powerpc_glow") + mesh.add_box((0.00, 0.36, 1.98), (0.14, 0.05, 0.12), "powerpc_glow") + mesh.add_box((0.26, 0.36, 1.98), (0.14, 0.05, 0.12), "powerpc_glow") + mesh.add_box((0.0, 0.0, 2.92), (0.66, 0.42, 0.16), "powerpc_case") + mesh.add_box((0.0, -0.02, 3.04), (0.36, 0.20, 0.08), "powerpc_dark") + return mesh + + +def write_mtl() -> None: + lines = ["# Material table for rustchain_vintage_powerpc_tower"] + for name, material in MATERIALS.items(): + kd = material["kd"] + lines.extend( + [ + f"newmtl {name}", + "Ka 0.000 0.000 0.000", + "Kd {:.3f} {:.3f} {:.3f}".format(*kd), + "Ks 0.050 0.050 0.050", + "Ns 16.0", + f"map_Kd {material['texture']}", + "", + ] + ) + MTL_FILE.write_text("\n".join(lines), encoding="ascii") + + +def write_skin() -> None: + lines = [ + f"{name},models/props/{PROP_NAME}/{material['texture']}" + for name, material in MATERIALS.items() + ] + SKIN_FILE.write_text("\n".join(lines) + "\n", encoding="ascii") + + +def write_preview() -> None: + image = Image.new("RGB", (1280, 720), (20, 24, 27)) + draw = ImageDraw.Draw(image) + draw.rectangle((80, 80, 520, 660), fill=(47, 51, 52), outline=(86, 96, 99), width=4) + draw.rectangle((185, 100, 415, 640), fill=MATERIALS["powerpc_case"]["color"], outline=(128, 117, 88), width=6) + draw.rectangle((225, 146, 375, 188), fill=MATERIALS["powerpc_dark"]["color"]) + draw.rectangle((235, 220, 365, 250), fill=MATERIALS["powerpc_dark"]["color"]) + for x in (232, 275, 318): + draw.rectangle((x, 305, x + 22, 488), fill=MATERIALS["powerpc_dark"]["color"]) + draw.rectangle((335, 360, 392, 420), fill=MATERIALS["powerpc_glow"]["color"]) + draw.text((341, 374), "RTC", fill=(5, 35, 38), font=font(16)) + stripe_y = 530 + for idx, color in enumerate([(220, 67, 57), (239, 167, 45), (86, 184, 96), (58, 137, 220)]): + draw.rectangle((230 + idx * 36, stripe_y, 258 + idx * 36, stripe_y + 24), fill=color) + draw.text((620, 112), "RustChain Vintage", fill=(238, 238, 230), font=font(39)) + draw.text((620, 158), "PowerPC Tower", fill=(238, 238, 230), font=font(39)) + draw.text((622, 218), "Original low-poly static IQM prop", fill=(168, 180, 180), font=font(25)) + draw.text((622, 258), "Bounty #14015 - vintage / retro tower slot", fill=(168, 180, 180), font=font(25)) + draw.text((622, 300), "Features", fill=(238, 238, 230), font=font(30)) + for i, line in enumerate( + [ + "Stacked optical and floppy bays", + "Vertical front cooling vents", + "Cyan RTC activity window", + "Side badge color stripes", + "Rear port hints and chunky feet", + ] + ): + draw.text((650, 352 + i * 38), "- " + line, fill=(204, 210, 204), font=font(23)) + for i, (name, material) in enumerate(MATERIALS.items()): + x = 630 + i * 140 + draw.rectangle((x, 580, x + 86, 640), fill=material["color"]) + draw.text((x, 650), name.replace("powerpc_", ""), fill=(190, 198, 196), font=font(18)) + image.save(PREVIEW_FILE) + + +def main() -> None: + OUT.mkdir(parents=True, exist_ok=True) + TEXTURES.mkdir(parents=True, exist_ok=True) + write_textures() + mesh = build_mesh() + mesh.write_obj(OBJ_FILE) + write_mtl() + write_skin() + write_preview() + (OUT / "README.md").write_text(README_TEXT, encoding="utf-8") + (OUT / "LICENSE").write_text(LICENSE_TEXT, encoding="utf-8") + print(f"Wrote prop source package to {OUT}") + print(f"Convert OBJ to IQM separately: {IQM_FILE}") + + +if __name__ == "__main__": + main()