|
|
""" |
|
|
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!") |
|
|
|