microsoft / microsoft/TRELLIS.2
Trellis generating duplicate inner mesh walls
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 11.3k
- Forks
- 1.4k
- PR merge metrics
- No merged PRs in 30d
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')
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing a TRELLIS.2 generation at 1024 resolution with a 2048 texture, then inspect the resulting mesh in Blender 5.1.0. Compare the duplicate inner walls with the supplied healing script and determine whether the defect originates in generation or mesh cleanup; done means the generated mesh no longer contains duplicate inner shells without introducing the random cleanup issues shown.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- blender, python
- Domain
- computer-graphics
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 38/100