| | """ |
| | Simple but VISIBLE 3D Geometry for KAPS |
| | ======================================== |
| | |
| | Replacing the complex geometry with SIMPLE, VISIBLE shapes: |
| | - CABLES: Thick line strips (not tubes that may fail) |
| | - AIRFOILS: Simple triangle meshes that are OBVIOUSLY wings |
| | - BUZZARD: Elongated shape that's NOT a sphere |
| | |
| | These MUST be visible. No complex vertex generation. |
| | """ |
| |
|
| | import numpy as np |
| | from typing import Tuple |
| |
|
| | try: |
| | from panda3d.core import ( |
| | GeomVertexFormat, GeomVertexData, GeomVertexWriter, |
| | Geom, GeomTriangles, GeomNode, GeomLines, GeomLinestrips, |
| | Vec3, Vec4, Point3, |
| | LineSegs, CardMaker |
| | ) |
| | PANDA3D_AVAILABLE = True |
| | except ImportError: |
| | PANDA3D_AVAILABLE = False |
| |
|
| |
|
| | def create_visible_buzzard( |
| | length: float = 12.0, |
| | width: float = 4.0, |
| | height: float = 3.0, |
| | color: Tuple[float, float, float, float] = (0.2, 0.4, 0.9, 1.0) |
| | ) -> GeomNode: |
| | """ |
| | Create a VISIBLE Buzzard - elongated fuselage shape. |
| | |
| | This is a simple hexagonal prism with tapered nose/tail. |
| | NOT A SPHERE. |
| | """ |
| | if not PANDA3D_AVAILABLE: |
| | return None |
| | |
| | format = GeomVertexFormat.getV3c4() |
| | vdata = GeomVertexData("buzzard", format, Geom.UHStatic) |
| | |
| | vertex = GeomVertexWriter(vdata, "vertex") |
| | col = GeomVertexWriter(vdata, "color") |
| | |
| | |
| | nose_color = (0.4, 0.5, 1.0, 1.0) |
| | body_color = color |
| | tail_color = (0.3, 0.3, 0.5, 1.0) |
| | |
| | |
| | |
| | vertex.addData3f(length/2, 0, 0) |
| | col.addData4f(*nose_color) |
| | |
| | |
| | fwd_x = length/4 |
| | fwd_r = width/2 * 0.6 |
| | for i in range(6): |
| | angle = i * np.pi / 3 |
| | y = fwd_r * np.cos(angle) |
| | z = fwd_r * np.sin(angle) * (height/width) |
| | vertex.addData3f(fwd_x, y, z) |
| | col.addData4f(*nose_color) |
| | |
| | |
| | mid_x = 0 |
| | mid_r = width/2 |
| | for i in range(6): |
| | angle = i * np.pi / 3 |
| | y = mid_r * np.cos(angle) |
| | z = mid_r * np.sin(angle) * (height/width) |
| | vertex.addData3f(mid_x, y, z) |
| | col.addData4f(*body_color) |
| | |
| | |
| | aft_x = -length/4 |
| | aft_r = width/2 * 0.7 |
| | for i in range(6): |
| | angle = i * np.pi / 3 |
| | y = aft_r * np.cos(angle) |
| | z = aft_r * np.sin(angle) * (height/width) |
| | vertex.addData3f(aft_x, y, z) |
| | col.addData4f(*tail_color) |
| | |
| | |
| | vertex.addData3f(-length/2, 0, 0) |
| | col.addData4f(*tail_color) |
| | |
| | |
| | prim = GeomTriangles(Geom.UHStatic) |
| | |
| | |
| | for i in range(6): |
| | i1 = 1 + i |
| | i2 = 1 + (i + 1) % 6 |
| | prim.addVertices(0, i1, i2) |
| | |
| | |
| | for i in range(6): |
| | f1 = 1 + i |
| | f2 = 1 + (i + 1) % 6 |
| | m1 = 7 + i |
| | m2 = 7 + (i + 1) % 6 |
| | prim.addVertices(f1, m1, f2) |
| | prim.addVertices(f2, m1, m2) |
| | |
| | |
| | for i in range(6): |
| | m1 = 7 + i |
| | m2 = 7 + (i + 1) % 6 |
| | a1 = 13 + i |
| | a2 = 13 + (i + 1) % 6 |
| | prim.addVertices(m1, a1, m2) |
| | prim.addVertices(m2, a1, a2) |
| | |
| | |
| | for i in range(6): |
| | i1 = 13 + i |
| | i2 = 13 + (i + 1) % 6 |
| | prim.addVertices(19, i2, i1) |
| | |
| | geom = Geom(vdata) |
| | geom.addPrimitive(prim) |
| | node = GeomNode("buzzard") |
| | node.addGeom(geom) |
| | return node |
| |
|
| |
|
| | def create_visible_airfoil( |
| | wingspan: float = 6.0, |
| | chord: float = 2.0, |
| | thickness: float = 0.5, |
| | color: Tuple[float, float, float, float] = (0.8, 0.8, 0.2, 1.0) |
| | ) -> GeomNode: |
| | """ |
| | Create a VISIBLE delta wing airfoil. |
| | |
| | This is a simple swept wing that is OBVIOUSLY a wing, not a blob. |
| | """ |
| | if not PANDA3D_AVAILABLE: |
| | return None |
| | |
| | format = GeomVertexFormat.getV3c4() |
| | vdata = GeomVertexData("airfoil", format, Geom.UHStatic) |
| | |
| | vertex = GeomVertexWriter(vdata, "vertex") |
| | col = GeomVertexWriter(vdata, "color") |
| | |
| | half_span = wingspan / 2 |
| | tip_chord = chord * 0.3 |
| | |
| | |
| | le_color = ( |
| | min(1.0, color[0] + 0.3), |
| | min(1.0, color[1] + 0.3), |
| | min(1.0, color[2] + 0.3), |
| | color[3] |
| | ) |
| | |
| | |
| | te_color = ( |
| | color[0] * 0.7, |
| | color[1] * 0.7, |
| | color[2] * 0.7, |
| | color[3] |
| | ) |
| | |
| | |
| | vertex.addData3f(chord, 0, thickness/2) |
| | col.addData4f(*le_color) |
| | |
| | vertex.addData3f(chord * 0.3, half_span, thickness/3) |
| | col.addData4f(*color) |
| | |
| | vertex.addData3f(chord * 0.3, -half_span, thickness/3) |
| | col.addData4f(*color) |
| | |
| | vertex.addData3f(-chord * 0.5, half_span * 0.3, thickness/4) |
| | col.addData4f(*te_color) |
| | |
| | vertex.addData3f(-chord * 0.5, -half_span * 0.3, thickness/4) |
| | col.addData4f(*te_color) |
| | |
| | vertex.addData3f(-chord * 0.3, 0, thickness/4) |
| | col.addData4f(*te_color) |
| | |
| | |
| | vertex.addData3f(chord, 0, -thickness/4) |
| | col.addData4f(*le_color) |
| | |
| | vertex.addData3f(chord * 0.3, half_span, -thickness/4) |
| | col.addData4f(*color) |
| | |
| | vertex.addData3f(chord * 0.3, -half_span, -thickness/4) |
| | col.addData4f(*color) |
| | |
| | vertex.addData3f(-chord * 0.5, half_span * 0.3, -thickness/4) |
| | col.addData4f(*te_color) |
| | |
| | vertex.addData3f(-chord * 0.5, -half_span * 0.3, -thickness/4) |
| | col.addData4f(*te_color) |
| | |
| | vertex.addData3f(-chord * 0.3, 0, -thickness/4) |
| | col.addData4f(*te_color) |
| | |
| | prim = GeomTriangles(Geom.UHStatic) |
| | |
| | |
| | prim.addVertices(0, 1, 3) |
| | prim.addVertices(0, 3, 5) |
| | prim.addVertices(0, 5, 4) |
| | prim.addVertices(0, 4, 2) |
| | prim.addVertices(1, 3, 5) |
| | prim.addVertices(2, 5, 4) |
| | |
| | |
| | prim.addVertices(6, 9, 7) |
| | prim.addVertices(6, 11, 9) |
| | prim.addVertices(6, 10, 11) |
| | prim.addVertices(6, 8, 10) |
| | prim.addVertices(7, 11, 9) |
| | prim.addVertices(8, 10, 11) |
| | |
| | |
| | prim.addVertices(0, 6, 1) |
| | prim.addVertices(1, 6, 7) |
| | prim.addVertices(0, 2, 6) |
| | prim.addVertices(2, 8, 6) |
| | |
| | |
| | prim.addVertices(3, 9, 5) |
| | prim.addVertices(5, 9, 11) |
| | prim.addVertices(4, 5, 10) |
| | prim.addVertices(5, 11, 10) |
| | |
| | |
| | prim.addVertices(1, 7, 3) |
| | prim.addVertices(3, 7, 9) |
| | prim.addVertices(2, 4, 8) |
| | prim.addVertices(4, 10, 8) |
| | |
| | geom = Geom(vdata) |
| | geom.addPrimitive(prim) |
| | node = GeomNode("airfoil") |
| | node.addGeom(geom) |
| | return node |
| |
|
| |
|
| | def create_visible_cable( |
| | start: np.ndarray, |
| | end: np.ndarray, |
| | color: Tuple[float, float, float, float] = (0.6, 0.5, 0.3, 1.0) |
| | ) -> GeomNode: |
| | """ |
| | Create a VISIBLE cable using thick lines. |
| | |
| | Returns a GeomNode with thick lines, not a complex tube mesh. |
| | """ |
| | if not PANDA3D_AVAILABLE: |
| | return None |
| | |
| | lines = LineSegs("cable") |
| | lines.setThickness(4.0) |
| | lines.setColor(*color) |
| | lines.moveTo(Point3(float(start[0]), float(start[1]), float(start[2]))) |
| | lines.drawTo(Point3(float(end[0]), float(end[1]), float(end[2]))) |
| | |
| | return lines.create() |
| |
|
| |
|
| | def create_visible_bola( |
| | radius: float = 3.0, |
| | color: Tuple[float, float, float, float] = (0.9, 0.6, 0.2, 1.0) |
| | ) -> GeomNode: |
| | """ |
| | Create a visible bola (consolidated TAB mass). |
| | |
| | This is a chunky octahedron-like shape that screams "I AM A HEAVY MASS". |
| | """ |
| | if not PANDA3D_AVAILABLE: |
| | return None |
| | |
| | format = GeomVertexFormat.getV3c4() |
| | vdata = GeomVertexData("bola", format, Geom.UHStatic) |
| | |
| | vertex = GeomVertexWriter(vdata, "vertex") |
| | col = GeomVertexWriter(vdata, "color") |
| | |
| | r = radius |
| | |
| | |
| | verts = [ |
| | (r, 0, 0), |
| | (-r, 0, 0), |
| | (0, r, 0), |
| | (0, -r, 0), |
| | (0, 0, r), |
| | (0, 0, -r), |
| | ] |
| | |
| | colors = [ |
| | (0.9, 0.5, 0.2, 1.0), |
| | (0.8, 0.4, 0.1, 1.0), |
| | (0.95, 0.6, 0.3, 1.0), |
| | (0.85, 0.45, 0.15, 1.0), |
| | (1.0, 0.7, 0.4, 1.0), |
| | (0.7, 0.35, 0.1, 1.0), |
| | ] |
| | |
| | for v, c in zip(verts, colors): |
| | vertex.addData3f(*v) |
| | col.addData4f(*c) |
| | |
| | prim = GeomTriangles(Geom.UHStatic) |
| | |
| | |
| | faces = [ |
| | (0, 2, 4), (2, 1, 4), (1, 3, 4), (3, 0, 4), |
| | (0, 5, 2), (2, 5, 1), (1, 5, 3), (3, 5, 0), |
| | ] |
| | |
| | for f in faces: |
| | prim.addVertices(*f) |
| | |
| | geom = Geom(vdata) |
| | geom.addPrimitive(prim) |
| | node = GeomNode("bola") |
| | node.addGeom(geom) |
| | return node |
| |
|
| |
|
| | def create_visible_grid_fin( |
| | size: float = 1.5, |
| | color: Tuple[float, float, float, float] = (0.5, 0.5, 0.6, 1.0) |
| | ) -> GeomNode: |
| | """ |
| | Create a visible grid fin - flat panel with grid pattern. |
| | """ |
| | if not PANDA3D_AVAILABLE: |
| | return None |
| | |
| | lines = LineSegs("gridfin") |
| | lines.setThickness(2.0) |
| | lines.setColor(*color) |
| | |
| | |
| | s = size |
| | grid = 4 |
| | |
| | for i in range(grid + 1): |
| | t = -s/2 + i * s / grid |
| | |
| | lines.moveTo(Point3(-s/2, 0, t)) |
| | lines.drawTo(Point3(s/2, 0, t)) |
| | |
| | lines.moveTo(Point3(t, 0, -s/2)) |
| | lines.drawTo(Point3(t, 0, s/2)) |
| | |
| | return lines.create() |
| |
|
| |
|
| | |
| | VISIBLE_TAB_COLORS = { |
| | "UP": (0.2, 1.0, 0.2, 1.0), |
| | "DOWN": (1.0, 0.2, 0.2, 1.0), |
| | "LEFT": (1.0, 1.0, 0.2, 1.0), |
| | "RIGHT": (1.0, 0.2, 1.0, 1.0), |
| | } |
| |
|
| | VISIBLE_CABLE_COLORS = { |
| | "slack": (0.3, 0.6, 0.3, 1.0), |
| | "taut": (0.8, 0.3, 0.2, 1.0), |
| | "normal": (0.5, 0.5, 0.4, 1.0), |
| | } |
| |
|
| |
|
| | if __name__ == "__main__": |
| | print("Testing simple geometry creation...") |
| | |
| | buz = create_visible_buzzard() |
| | print(f"Buzzard: {buz}") |
| | |
| | air = create_visible_airfoil() |
| | print(f"Airfoil: {air}") |
| | |
| | bola = create_visible_bola() |
| | print(f"Bola: {bola}") |
| | |
| | print("All geometry created successfully!") |
| |
|