diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-22 00:29:43 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-22 00:29:43 +0800 |
| commit | dc454966bcacabb7df032edfda51759ecd9d3d94 (patch) | |
| tree | 8249ee79abe67734d87128f62b331e797b8c7feb | |
| parent | 9ec32a2ae03aabdae2828aa87a6a7689fedb96a6 (diff) | |
| download | somhairles-dream-fsharp-dc454966bcacabb7df032edfda51759ecd9d3d94.tar.gz | |
feat(ship): PLA Space Force livery (deep-space blue, twin gold stripes, red star), crew details, aft transition structure; remove workspace grid and axis readout
| -rw-r--r-- | public/App.js | 3 | ||||
| -rw-r--r-- | public/index.html | 3 | ||||
| -rw-r--r-- | src/SomhairlesDream.Frontend/App.fs | 6 | ||||
| -rw-r--r-- | src/SomhairlesDream.Modeling/Definitions.fs | 7 | ||||
| -rw-r--r-- | stage/blender/ship.py | 104 |
5 files changed, 101 insertions, 22 deletions
diff --git a/public/App.js b/public/App.js index f9e8de3..492950b 100644 --- a/public/App.js +++ b/public/App.js @@ -350,9 +350,6 @@ export function initializeView() { const starMaterial = new THREE.PointsMaterial({ color: "#c9d6e6", size: 1.7, sizeAttenuation: true, transparent: true, opacity: 0.9, depthWrite: false }); const stars = new THREE.Points(starGeometry, starMaterial); scene.add(stars); - const grid = new THREE.GridHelper(24, 48, "#5a4a2a", "#2a2f33"); - grid.position.y = -1.35; - scene.add(grid); const controls = new OrbitControls(camera, canvas); controls.target.copy({ x: 0, y: 0, z: 0 }); controls.enableDamping = true; diff --git a/public/index.html b/public/index.html index 71f67b1..9664605 100644 --- a/public/index.html +++ b/public/index.html @@ -89,7 +89,6 @@ </div> <div class="viewport-frame"> <canvas id="viewport-canvas" aria-label="设计几何三维视图"></canvas> - <div class="axis-readout" aria-hidden="true">X / Y / Z</div> <div id="viewport-note" class="viewport-note">实时几何 · V00</div> <div id="viewport-loading" class="viewport-loading is-hidden" aria-hidden="true"> <span class="loading-ring"></span> @@ -140,6 +139,6 @@ <span>heritage 参考线保留占位演示 · F# / OPEN CODE</span> </footer> </main> - <script type="module" src="/App.js?v=ui11"></script> + <script type="module" src="/App.js?v=ui12"></script> </body> </html> diff --git a/src/SomhairlesDream.Frontend/App.fs b/src/SomhairlesDream.Frontend/App.fs index 2aa78eb..40f3a80 100644 --- a/src/SomhairlesDream.Frontend/App.fs +++ b/src/SomhairlesDream.Frontend/App.fs @@ -297,12 +297,6 @@ let initializeView () = let stars = createPoints starGeometry starMaterial scene.add(stars) - // Coordinate ground grid so the model reads as anchored in a workspace - // instead of floating in a void. - let grid = createGridHelper (24.0, 48, "#5a4a2a", "#2a2f33") - setNodePositionY (grid, -1.35) - scene.add(grid) - let controls = createOrbitControls camera canvas setControlsTarget controls 0.0 0.0 0.0 setControlsDamping controls true diff --git a/src/SomhairlesDream.Modeling/Definitions.fs b/src/SomhairlesDream.Modeling/Definitions.fs index bb6543c..646fcfe 100644 --- a/src/SomhairlesDream.Modeling/Definitions.fs +++ b/src/SomhairlesDream.Modeling/Definitions.fs @@ -115,7 +115,8 @@ module Design = [| "hnx02.cabin.airlock"; "hnx02.shuttle.orion" |] let livery = [| "hnx02.detail.greeble"; "hnx02.detail.antenna" - "hnx02.detail.glow"; "hnx02.detail.glass" |] + "hnx02.detail.glow"; "hnx02.detail.glass" + "hnx02.livery.stripes"; "hnx02.livery.insignia" |] let prior (prefix: string array) (current: string array) = Array.append prefix current @@ -145,7 +146,9 @@ module Design = [| { ObjectId = "hnx02.detail.greeble"; Kind = "add"; Summary = "Add equipment rows, cable looms and navigation lights" } { ObjectId = "hnx02.detail.antenna"; Kind = "add"; Summary = "Add high-gain communication antennas" } { ObjectId = "hnx02.detail.glow"; Kind = "add"; Summary = "Add engine bell heat glow" } - { ObjectId = "hnx02.detail.glass"; Kind = "add"; Summary = "Add lit habitat viewports" } |] + { ObjectId = "hnx02.detail.glass"; Kind = "add"; Summary = "Add lit habitat viewports" } + { ObjectId = "hnx02.livery.stripes"; Kind = "add"; Summary = "Add twin gold flank stripes (PLA Space Force livery)" } + { ObjectId = "hnx02.livery.insignia"; Kind = "add"; Summary = "Add red star insignia" } |] [| { StepId = "ship-01-keel"; Index = 0; ObjectIds = keel; Changes = keelChanges } { StepId = "ship-02-hab"; Index = 1; ObjectIds = prior keel hab; Changes = habChanges } diff --git a/stage/blender/ship.py b/stage/blender/ship.py index 8a67673..9ba04a4 100644 --- a/stage/blender/ship.py +++ b/stage/blender/ship.py @@ -125,12 +125,14 @@ STEP_OBJECTS = { "hnx02.detail.antenna", "hnx02.detail.glow", "hnx02.detail.glass", + "hnx02.livery.stripes", + "hnx02.livery.insignia", ], } # Material keys per object id. OBJECT_MATERIAL = { - "hnx02.hull.core": "white", + "hnx02.hull.core": "hull", "hnx02.spine.truss": "hull", "hnx02.node.forward": "white", "hnx02.hab.a": "white", @@ -150,6 +152,8 @@ OBJECT_MATERIAL = { "hnx02.detail.antenna": "hull", "hnx02.detail.glow": "glow", "hnx02.detail.glass": "glass", + "hnx02.livery.stripes": "gold", + "hnx02.livery.insignia": "insignia", } @@ -185,18 +189,20 @@ def make_material(bpy, name, color, *, metallic=0.0, roughness=0.45, emission=No def build_materials(bpy): return { - "hull": make_material(bpy, "hnx02.hull", (0.62, 0.64, 0.66), metallic=0.88, roughness=0.38), - "white": make_material(bpy, "hnx02.white", (0.86, 0.86, 0.84), metallic=0.18, roughness=0.52), + # PLA Space Force livery: "deep-space blue" hull (深空蓝) with gold accents. + "hull": make_material(bpy, "hnx02.hull", (0.035, 0.070, 0.230), metallic=0.60, roughness=0.44), + "white": make_material(bpy, "hnx02.white", (0.80, 0.82, 0.85), metallic=0.18, roughness=0.52), "shield": make_material(bpy, "hnx02.shield", (0.44, 0.46, 0.50), metallic=0.30, roughness=0.72), "dark": make_material(bpy, "hnx02.dark", (0.10, 0.11, 0.13), metallic=0.72, roughness=0.48), "radiator": make_material(bpy, "hnx02.radiator", (0.90, 0.91, 0.92), metallic=0.08, roughness=0.30), "pv": make_material(bpy, "hnx02.pv", (0.07, 0.10, 0.24), metallic=0.35, roughness=0.28), - "gold": make_material(bpy, "hnx02.gold", (0.82, 0.58, 0.19), metallic=1.0, roughness=0.30), + "gold": make_material(bpy, "hnx02.gold", (0.86, 0.63, 0.16), metallic=1.0, roughness=0.28), + "insignia": make_material(bpy, "hnx02.insignia", (0.72, 0.09, 0.09), metallic=0.15, roughness=0.45), "copper": make_material(bpy, "hnx02.copper", (0.70, 0.44, 0.19), metallic=1.0, roughness=0.34), "glow": make_material(bpy, "hnx02.glow", (1.0, 0.35, 0.10), metallic=0.0, roughness=0.6, emission=(1.0, 0.42, 0.14), emission_strength=9.0), - "glass": make_material(bpy, "hnx02.glass", (0.55, 0.75, 0.95), metallic=0.0, roughness=0.25, - emission=(0.55, 0.80, 1.0), emission_strength=4.5), + "glass": make_material(bpy, "hnx02.glass", (0.95, 0.84, 0.55), metallic=0.0, roughness=0.25, + emission=(0.98, 0.86, 0.52), emission_strength=5.0), } @@ -764,8 +770,8 @@ def b_hull_core(bm): a = 2.0 * math.pi * k / count radial = Vector((math.cos(a), 0.0, math.sin(a))) wc = c0 + radial * r - cyl(bm, wc - radial * 0.12, wc + radial * 0.18, 0.22, seg=12, cap1=False) - torus(bm, wc + radial * 0.18, 0.245, 0.05, axis=radial, seg=14, rseg=5) + cyl(bm, wc - radial * 0.12, wc + radial * 0.20, 0.28, seg=12, cap1=False) + torus(bm, wc + radial * 0.20, 0.305, 0.055, axis=radial, seg=14, rseg=5) panel_bank(bm, p1 + Vector((0.0, 3.5, 0.0)), p2 - Vector((0.0, 3.5, 0.0)), r + 0.035, cols=72, rows=18, depth=0.05) for (t0, count, seed) in ((0.10, 26, 601), (0.60, 30, 602), (0.90, 22, 603)): @@ -892,6 +898,26 @@ def b_reactor(bm): def b_thrust_cone(bm): + # Aft transition structure: heavy beams + ring frames that visibly tie the + # hull to the propulsion section instead of leaving a bare truss gap. + for k in range(4): + a = math.pi / 4.0 + k * math.pi / 2.0 + radial = Vector((math.cos(a), 0.0, math.sin(a))) + cyl(bm, Vector((0.0, -3.0, 0.0)) + radial * (HULL_R * 0.92), + Vector((0.0, -30.0, 0.0)) + radial * 2.6, 0.26, seg=10) + for t in (0.12, 0.32, 0.52, 0.72, 0.9): + yy = -3.0 - 27.0 * t + rr = HULL_R * 0.92 + (2.6 - HULL_R * 0.92) * t + torus(bm, Vector((0.0, yy, 0.0)), rr, 0.11, axis=(0, 1, 0), seg=24, rseg=6) + # propellant feed lines: tanks -> reactor -> engines + for sx in (-1.0, 1.0): + for dz in (0.6, -0.6): + pipe_run(bm, [ + (sx * TANK_X, -25.0, dz * TANK_R), + (sx * 4.6, -31.0, dz * 2.3), + (sx * 2.2, -36.0, dz * 1.5), + (sx * 1.3, -40.0, dz * 0.9), + ], 0.15) thrust_cone(bm, (0.0, -33.5, 0.0), (0, -1, 0), 3.6, 2.9, 1.8, struts=14) shadow_shield(bm, (0.0, -35.6, 0.0), (0, -1, 0), 4.4, seg=36, ribs=12) @@ -974,6 +1000,51 @@ def b_orion(bm): bell(bm, base - Vector((0, 0.6, 0)), (0, -1, 0), 1.5, 0.22, 0.68, seg=22, layers=9) +def five_star(bm, center, normal, radius, depth=0.09): + """Filled five-pointed star stamped on the hull (PLA insignia).""" + n = Vector(normal).normalized() + x, y, _ = basis(n) + c = Vector(center) + front, back = [], [] + for i in range(10): + a = math.pi / 2.0 + i * math.pi / 5.0 + rr = radius if i % 2 == 0 else radius * 0.382 + p = c + (x * math.cos(a) + y * math.sin(a)) * rr + front.append(bm.verts.new(p + n * depth * 0.5)) + back.append(bm.verts.new(p - n * depth * 0.5)) + cf = bm.verts.new(c + n * depth * 0.5) + cb = bm.verts.new(c - n * depth * 0.5) + for i in range(10): + j = (i + 1) % 10 + bm.faces.new((cf, front[i], front[j])) + bm.faces.new((cb, back[j], back[i])) + bm.faces.new((front[i], back[i], back[j], front[j])) + + +def b_stripes(bm): + """Twin gold stripes down both flanks — PLA Space Force colour band.""" + p1, p2 = Vector(HULL_P1), Vector(HULL_P2) + length = (p2 - p1).length + for base_deg in (0.0, 180.0): + for offset in (-20.0, 20.0): + a = math.radians(base_deg + offset) + radial = Vector((math.cos(a), 0.0, math.sin(a))) + tangent = radial.cross(Vector((0.0, 1.0, 0.0))).normalized() + c = (p1 + p2) * 0.5 + radial * (HULL_R + 0.14) + box(bm, c, 0.035, length * 0.455, 0.26, radial, Vector((0.0, 1.0, 0.0)), tangent) + + +def b_insignia(bm): + """Red star insignia placed where nothing occludes them.""" + for sx in (-1.0, 1.0): + a = 0.0 if sx > 0.0 else math.pi + radial = Vector((math.cos(a), 0.0, math.sin(a))) + # forward hull, in the clear band between the water wall and the node + five_star(bm, Vector((0.0, 40.2, 0.0)) + radial * (HULL_R + 0.06), radial, 1.9, 0.13) + # outboard face of the side-mounted modules + five_star(bm, Vector((sx * (6.6 + 2.6 + 0.05), 21.0, 0.0)), (sx, 0.0, 0.0), 1.5, 0.11) + + def b_glow(bm): """Heat glow rings inside the engine bells.""" for ex in (-2.4, 2.4): @@ -995,7 +1066,7 @@ def b_glass(bm): a = 2.0 * math.pi * k / count radial = Vector((math.cos(a), 0.0, math.sin(a))) wc = c0 + radial * (HULL_R + 0.18) - cyl(bm, wc - radial * 0.02, wc + radial * 0.05, 0.17, seg=10) + cyl(bm, wc - radial * 0.02, wc + radial * 0.06, 0.23, seg=10) def b_greeble(bm): @@ -1020,6 +1091,19 @@ def b_greeble(bm): cyl(bm, Vector(pos), Vector(pos) + d * 0.15, rad, seg=10) cyl(bm, Vector(pos) + d * 0.15, Vector(pos) + d * 0.34, rad * 0.7, seg=10) equipment_row(bm, (0.0, 34.0, r + 0.15), (0, 1, 0), (0, 0, 1), 12, 1.0, 0.10, 0.24, seed=501) + # EVA handrail runs (crew translation paths along the hull) + for sx in (-1.0, 1.0): + for (yy0, count) in ((24.0, 10), (4.0, 12)): + a = math.radians(-14.0 if sx > 0.0 else 194.0) + radial = Vector((math.cos(a), 0.0, math.sin(a))) + tangent = Vector((0.0, 1.0, 0.0)) + for k in range(count): + yy = yy0 + k * 1.35 + base = Vector((0.0, yy, 0.0)) + radial * HULL_R + tip = base + radial * 0.42 + cyl(bm, base, tip, 0.045, seg=6) + if k < count - 1: + cyl(bm, tip, tip + tangent * 1.35, 0.035, seg=5) equipment_row(bm, (0.0, 6.0, -r - 0.15), (0, 1, 0), (0, 0, -1), 14, 1.0, 0.10, 0.24, seed=502) @@ -1052,6 +1136,8 @@ BUILDERS = { "hnx02.detail.antenna": b_antenna, "hnx02.detail.glow": b_glow, "hnx02.detail.glass": b_glass, + "hnx02.livery.stripes": b_stripes, + "hnx02.livery.insignia": b_insignia, } |
