Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ def _selection_path(filename):
"front_flip": False,
"output_format": "png",
"label_parts": False,
"show_hidden_lines": False,
"ao_mode": "ssao",
"keep_uploads": False,
}
Expand Down Expand Up @@ -301,6 +302,7 @@ def stream():
front_flip = bool(data.get("front_flip", False))
rotation_deg = float(data.get("rotation_deg", 0) or 0)
label_parts = bool(data.get("label_parts", False))
show_hidden_lines = bool(data.get("show_hidden_lines", False))
keep_uploads = bool(data.get("keep_uploads", False))
# "vertex" (fast: per-vertex baked AO, can look blocky on large
# flat panels) or "ssao" (default: screen-space AO computed
Expand All @@ -324,6 +326,7 @@ def stream():
"ao": ao_enabled, "ao_darkness": ao_darkness, "up_axis": up_axis,
"front_flip": front_flip, "rotation_deg": rotation_deg,
"output_format": output_format, "label_parts": label_parts,
"show_hidden_lines": show_hidden_lines,
"ao_mode": ao_mode, "keep_uploads": keep_uploads,
})

Expand Down Expand Up @@ -396,6 +399,7 @@ def stream():
resolution=render_res, ao_mesh=ao_mesh,
ao_mode=(ao_mode if ao_enabled else None),
ao_max_darkness=ao_darkness / 100.0,
show_hidden_lines=show_hidden_lines,
)
if part_centroids:
r = view_results[v]
Expand Down
29 changes: 26 additions & 3 deletions renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,12 +300,12 @@ def _smooth_closed_contour(coords, iterations=2):


def render_view(mesh, view_name, center, half_span, dist, axis_cfg,
resolution=1800, n_samples=30, depth_eps=0.018,
resolution=1800, n_samples=30, depth_eps=None,
crease_angle_deg=25.0, ao_mesh=None, ao_levels=9,
min_contour_area=60, ao_blur_sigma_px=1.4,
ao_mode=None, ao_max_darkness=0.5,
smooth_contours=True, smooth_iterations=1,
contour_supersample=4):
contour_supersample=4, show_hidden_lines=False):
"""Renders one orthographic view of mesh, returning a dict with:
outer_contours: list of Nx2 pixel-space silhouette contours
ao_raster: (gray uint8 array, alpha bool mask) for AO shading as a
Expand All @@ -323,8 +323,24 @@ def render_view(mesh, view_name, center, half_span, dist, axis_cfg,
ao_mode: None (no AO), "vertex" (use ao_mesh's baked vertex colors), or
"ssao" (screen-space AO computed directly from THIS view's own depth
buffer — no ao_mesh needed, genuinely per-view, recomputed every call).

depth_eps: tolerance (world units) used by compute_visible_edges() to
decide whether an edge sample is at the same depth as the nearest
surface there (visible) or meaningfully behind it (hidden). Defaults to
None, which derives it from half_span instead of using a fixed number --
a fixed absolute default breaks down for models exported at a very
different unit scale than the "normal-size car" it was tuned for (e.g.
a whole model just a few centimeters across): the tolerance can end up
larger than the model itself, so the visibility test never hides
anything and interior geometry bleeds through as visible lines.

show_hidden_lines: if True, skips the occlusion test entirely and keeps
every feature/boundary edge regardless of what's in front of it (full
wireframe). The silhouette is still computed normally either way.
"""
znear, zfar = 0.01, dist * 2.2
if depth_eps is None:
depth_eps = half_span * 0.0075
eye, up = _view_geometry(view_name, center, dist, axis_cfg)

# Feature edges (dihedral angle) + true mesh boundaries. On fragmented
Expand All @@ -343,7 +359,14 @@ def render_view(mesh, view_name, center, half_span, dist, axis_cfg,
except Exception:
edges_v = np.empty((0, 2), dtype=int)

if len(edges_v) > 0:
if show_hidden_lines:
# Still need a depth render for the silhouette, just not for edge
# visibility -- every feature/boundary edge is kept as-is.
depth_raw, pose, cam = render_depth(mesh_welded, eye, center, up, half_span, half_span,
resolution=resolution, znear=znear, zfar=zfar)
depth_true = pyrender_depth_to_true_distance(depth_raw, znear, zfar)
segs = list(zip(mesh_welded.vertices[edges_v[:, 0]], mesh_welded.vertices[edges_v[:, 1]])) if len(edges_v) > 0 else []
elif len(edges_v) > 0:
segs, depth_true, pose, cam = compute_visible_edges(
mesh_welded, edges_v, eye, center, up, half_span, half_span,
resolution=resolution, n_samples=n_samples, znear=znear, zfar=zfar, depth_eps=depth_eps
Expand Down
5 changes: 5 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ <h2 style="margin-top:20px;">5. Detail &amp; Output</h2>
Labels are placed at each part's center point in every view. Parts
hidden behind other geometry still get a label there.
</p>
<div class="color_row" style="margin-top:10px;">
<label><input type="checkbox" id="show_hidden_lines_cb"> Show hidden lines (full wireframe, ignores what's blocked from view)</label>
</div>
</div>

<div class="section hidden" id="generate_section">
Expand Down Expand Up @@ -285,6 +288,7 @@ <h2 style="margin-top:20px;">5. Detail &amp; Output</h2>
document.getElementById('rotation_deg_input').value = prefs.rotation_deg || 0;
document.getElementById('output_format_select').value = prefs.output_format || 'png';
document.getElementById('label_parts_cb').checked = !!prefs.label_parts;
document.getElementById('show_hidden_lines_cb').checked = !!prefs.show_hidden_lines;
document.getElementById('keep_uploads_cb').checked = !!prefs.keep_uploads;
}

Expand Down Expand Up @@ -422,6 +426,7 @@ <h2 style="margin-top:20px;">5. Detail &amp; Output</h2>
rotation_deg: parseFloat(document.getElementById('rotation_deg_input').value) || 0,
output_format: document.getElementById('output_format_select').value,
label_parts: document.getElementById('label_parts_cb').checked,
show_hidden_lines: document.getElementById('show_hidden_lines_cb').checked,
keep_uploads: document.getElementById('keep_uploads_cb').checked,
};

Expand Down