import gradio as gr import numpy as np import plotly.graph_objects as go from pyvox.models import Vox from pyvox.writer import VoxWriter from pyvox.parser import VoxParser def load_vox_model(file_path): """Load and parse a .vox file""" try: print(f"Attempting to parse vox file: {file_path}") model = VoxParser(file_path).parse() print(f"Model parsed successfully") voxels = model.to_dense() print(f"Voxel array shape: {voxels.shape}") print(f"Number of non-zero voxels: {np.count_nonzero(voxels)}") print(f"Palette size: {len(model.palette)}") if np.count_nonzero(voxels) == 0: print("Warning: No voxels found in the model") return None, None return voxels, model.palette except Exception as e: print(f"Error in load_vox_model: {str(e)}") return None, None def create_3d_scatter(voxels, palette): """Create a 3D scatter plot from voxel data""" # Get coordinates and color indices of non-zero voxels x, y, z = np.nonzero(voxels) color_indices = voxels[x, y, z] - 1 # Subtract 1 since palette indices in .vox start at 1 # Apply rotation to orient the model correctly # Negate y coordinate to flip the model upright and rotate z to face forward y = -y temp_z = z.copy() z = -temp_z # Negate z to make the model face forward # Convert palette indices to RGB colors using direct palette indexing rgb_colors = [f'rgb({int(palette[c][0])}, {int(palette[c][1])}, {int(palette[c][2])})' if c < len(palette) else 'rgb(255, 255, 255)' for c in color_indices] # Create 3D scatter plot with improved voxel representation fig = go.Figure(data=[go.Scatter3d( x=x, y=y, z=z, mode='markers', marker=dict( size=6, # Reduced size for better visualization color=rgb_colors, opacity=1.0, symbol='square', # Using square symbol (supported by Plotly) line=dict(width=0), # Remove border lines completely sizemode='diameter', # Fixed size regardless of zoom sizeref=1.0 # Reference scale for consistent size ) )]) # Calculate center and range for better camera positioning center_x = (x.max() + x.min()) / 2 center_y = (y.max() + y.min()) / 2 center_z = (z.max() + z.min()) / 2 max_range = max(x.max() - x.min(), y.max() - y.min(), z.max() - z.min()) # Update layout for better visualization fig.update_layout( scene=dict( aspectmode='cube', # Force cubic aspect ratio camera=dict( up=dict(x=0, y=1, z=0), center=dict(x=0, y=0, z=0), eye=dict(x=1.5, y=0.9, z=0.9) # Adjusted camera position for a closer view while maintaining angle ), xaxis=dict(range=[center_x - max_range/1.5, center_x + max_range/1.5], showgrid=True, gridwidth=1, gridcolor='rgba(128, 128, 128, 0.2)'), yaxis=dict(range=[center_y - max_range/1.5, center_y + max_range/1.5], showgrid=True, gridwidth=1, gridcolor='rgba(128, 128, 128, 0.2)'), zaxis=dict(range=[center_z - max_range/1.5, center_z + max_range/1.5], showgrid=True, gridwidth=1, gridcolor='rgba(128, 128, 128, 0.2)') ), margin=dict(l=0, r=0, t=0, b=0), showlegend=False, template='plotly_dark', # Dark theme for better contrast paper_bgcolor='rgba(0,0,0,1)', # Pure black background plot_bgcolor='rgba(0,0,0,1)' ) return fig def display_vox_model(vox_file): """Main function to display the voxel model""" try: # Load the vox model if not vox_file: return "Please upload a .vox file" if not vox_file.name.endswith('.vox'): return "Please upload a valid .vox file" print(f"Loading vox file: {vox_file.name}") voxels, palette = load_vox_model(vox_file.name) if voxels is None or palette is None: return "Error: Could not load voxel data from file" if voxels.size == 0: return "Error: No voxels found in the model" print(f"Model loaded successfully. Shape: {voxels.shape}, Palette size: {len(palette)}") # Create the 3D visualization fig = create_3d_scatter(voxels, palette) return fig except Exception as e: print(f"Error details: {str(e)}") return f"Error loading model: {str(e)}" # Create Gradio interface interface = gr.Interface( fn=display_vox_model, inputs=gr.File(label="Upload .vox file"), outputs=gr.Plot(label="3D Voxel Model"), title="Voxel Model Viewer", description="Upload a .vox file to view the 3D voxelized model.", examples=[["modelo_optimizado.vox"]] ) if __name__ == "__main__": interface.launch()