-
Notifications
You must be signed in to change notification settings - Fork 588
Trellis generating duplicate inner mesh walls #140
Copy link
Copy link
Open
Description
Most generations add an entire inner shell of the mesh.
I was able to create a function in blender that can 99% remove the inner shell but results in some random issues
Stats on Model post Trellis2 Generation at 1024 Resolution and 2048 Texture Size
POST TRELLIS 2 Generation:
Post Healing Script - Removing Inner Walls:
Blender 5.1.0
Base Blender Script used:
import bpy
import bmesh
from mathutils.bvhtree import BVHTree
class GeometryAlgorithm:
Mathematical targeted healing of AI mesh anomalies.
@staticmethod
def heal_geometry(obj):
if bpy.context.active_object and bpy.context.active_object.mode != 'OBJECT':
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
obj.hide_set(False)
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
# 1. Apply Transforms (Crucial for Mixamo auto-rigger)
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
bpy.ops.mesh.customdata_custom_splitnormals_clear()
bpy.ops.object.mode_set(mode='EDIT')
# ==========================================
# --- PASS 1: LINE-OF-SIGHT SEED & SPREAD ---
# ==========================================
bm = bmesh.from_edit_mesh(obj.data)
bm.faces.ensure_lookup_table()
# Deselect everything to prepare for seeding
for f in bm.faces:
f.select = False
verts_co = [v.co for v in bm.verts]
faces_indices = [[v.index for v in f.verts] for f in bm.faces]
tree = BVHTree.FromPolygons(verts_co, faces_indices)
seed_count = 0
# 1. Raycast Seed: If a face's normal points directly at the sky, it's 100% outer shell.
for f in bm.faces:
# Nudge the origin slightly to prevent hitting the face itself
origin = f.calc_center_median() + (f.normal * 0.0001)
loc, _, _, _ = tree.ray_cast(origin, f.normal)
if loc is None:
f.select = True
seed_count += 1
# 2. Extreme Bounds Seed: An absolute guarantee to find the outer limits of the mesh
if bm.verts:
extremes = [
min(bm.verts, key=lambda v: v.co.z), max(bm.verts, key=lambda v: v.co.z),
min(bm.verts, key=lambda v: v.co.x), max(bm.verts, key=lambda v: v.co.x),
min(bm.verts, key=lambda v: v.co.y), max(bm.verts, key=lambda v: v.co.y)
]
for v in extremes:
for f in v.link_faces:
if not f.select:
f.select = True
seed_count += 1
bmesh.update_edit_mesh(obj.data)
if seed_count > 0:
# 3. The Spread: Select everything connected to the outer shell seeds!
# This perfectly protects backpacks, deep armpits, and tight crevices.
bpy.ops.mesh.select_linked()
# 4. The Purge: Delete everything that is detached/inside (The Phantom Inner Wall)
bpy.ops.mesh.select_all(action='INVERT')
bpy.ops.mesh.delete(type='FACE')
# ==========================================
# --- PASS 2: SURGICAL BMESH STRAY EDGE SNIP ---
# ==========================================
# Re-initialize bmesh after the face deletion
bm = bmesh.from_edit_mesh(obj.data)
for _ in range(2):
bm.edges.ensure_lookup_table()
bm.verts.ensure_lookup_table()
stray_edges = [e for e in bm.edges if len(e.link_faces) == 0]
if stray_edges:
bmesh.ops.delete(bm, geom=stray_edges, context='EDGES')
stray_verts = [v for v in bm.verts if len(v.link_faces) == 0]
if stray_verts:
bmesh.ops.delete(bm, geom=stray_verts, context='VERTS')
bmesh.update_edit_mesh(obj.data)
# ==========================================
# --- PASS 3: SAFE MACRO GEOMETRY HEALING ---
# ==========================================
for _ in range(3):
bpy.ops.mesh.select_all(action='SELECT')
# Since the phantom inner shell was vaporized, it is now
# 100% safe to run the vertex merge without the walls pinching together!
bpy.ops.mesh.remove_doubles(threshold=0.0005)
# Blender's native interior face selector handles any remaining micro-webbing
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.select_interior_faces()
bpy.ops.mesh.delete(type='FACE')
# Instantly bridges any holes shut left by the purge.
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.fill_holes(sides=100)
# ==========================================
# --- PASS 4: DEGENERATE FACE CLEANUP ---
# ==========================================
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.delete_loose()
# Safely eliminates the 88k micro-faces AI tools generate
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.dissolve_degenerate(threshold=0.002)
# Force all normals outward to fix Mixamo voxelizer holes
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.normals_make_consistent(inside=False)
# ==========================================
# --- CLEANUP ---
# ==========================================
try:
bpy.ops.object.mode_set(mode='SCULPT')
bpy.ops.paint.mask_clear()
except Exception:
pass
bpy.ops.object.mode_set(mode='OBJECT')
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels