voxel-viewer / viewer.py
Miguelpef's picture
Upload app, readme, requirements and example model
7ce9c01 verified
raw
history blame
4.98 kB
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()