Spaces:
Sleeping
Sleeping
Upload app, readme, requirements and example model
Browse files- README.md +28 -14
- modelo_optimizado.vox +0 -0
- requirements.txt +4 -0
- viewer.py +128 -0
README.md
CHANGED
@@ -1,14 +1,28 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Voxel Model Viewer
|
2 |
+
|
3 |
+
A web application built with Gradio that allows users to view and interact with 3D voxel models in .vox format.
|
4 |
+
|
5 |
+
## Features
|
6 |
+
|
7 |
+
- Upload and view .vox files in 3D
|
8 |
+
- Interactive 3D visualization with rotation and zoom
|
9 |
+
- Dark theme interface for better contrast
|
10 |
+
- Support for colored voxels using the model's palette
|
11 |
+
|
12 |
+
## Usage
|
13 |
+
|
14 |
+
1. Upload a .vox file using the file upload interface
|
15 |
+
2. The model will be displayed in an interactive 3D viewer
|
16 |
+
3. Use mouse controls to rotate and zoom the model
|
17 |
+
|
18 |
+
## Technical Details
|
19 |
+
|
20 |
+
This application uses:
|
21 |
+
- Gradio for the web interface
|
22 |
+
- PyVox for parsing .vox files
|
23 |
+
- Plotly for 3D visualization
|
24 |
+
- NumPy for array operations
|
25 |
+
|
26 |
+
## Example
|
27 |
+
|
28 |
+
An example .vox file (modelo_optimizado.vox) is included in the repository for testing purposes.
|
modelo_optimizado.vox
ADDED
Binary file (51.5 kB). View file
|
|
requirements.txt
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
gradio>=4.0.0
|
2 |
+
numpy>=1.24.0
|
3 |
+
plotly>=5.18.0
|
4 |
+
pyvox>=1.0.0
|
viewer.py
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import numpy as np
|
3 |
+
import plotly.graph_objects as go
|
4 |
+
from pyvox.models import Vox
|
5 |
+
from pyvox.writer import VoxWriter
|
6 |
+
from pyvox.parser import VoxParser
|
7 |
+
|
8 |
+
def load_vox_model(file_path):
|
9 |
+
"""Load and parse a .vox file"""
|
10 |
+
try:
|
11 |
+
print(f"Attempting to parse vox file: {file_path}")
|
12 |
+
model = VoxParser(file_path).parse()
|
13 |
+
print(f"Model parsed successfully")
|
14 |
+
|
15 |
+
voxels = model.to_dense()
|
16 |
+
print(f"Voxel array shape: {voxels.shape}")
|
17 |
+
print(f"Number of non-zero voxels: {np.count_nonzero(voxels)}")
|
18 |
+
print(f"Palette size: {len(model.palette)}")
|
19 |
+
|
20 |
+
if np.count_nonzero(voxels) == 0:
|
21 |
+
print("Warning: No voxels found in the model")
|
22 |
+
return None, None
|
23 |
+
|
24 |
+
return voxels, model.palette
|
25 |
+
except Exception as e:
|
26 |
+
print(f"Error in load_vox_model: {str(e)}")
|
27 |
+
return None, None
|
28 |
+
|
29 |
+
def create_3d_scatter(voxels, palette):
|
30 |
+
"""Create a 3D scatter plot from voxel data"""
|
31 |
+
# Get coordinates and color indices of non-zero voxels
|
32 |
+
x, y, z = np.nonzero(voxels)
|
33 |
+
color_indices = voxels[x, y, z] - 1 # Subtract 1 since palette indices in .vox start at 1
|
34 |
+
|
35 |
+
# Apply rotation to orient the model correctly
|
36 |
+
# Negate y coordinate to flip the model upright and rotate z to face forward
|
37 |
+
y = -y
|
38 |
+
temp_z = z.copy()
|
39 |
+
z = -temp_z # Negate z to make the model face forward
|
40 |
+
|
41 |
+
# Convert palette indices to RGB colors using direct palette indexing
|
42 |
+
rgb_colors = [f'rgb({int(palette[c][0])}, {int(palette[c][1])}, {int(palette[c][2])})'
|
43 |
+
if c < len(palette) else 'rgb(255, 255, 255)' for c in color_indices]
|
44 |
+
|
45 |
+
# Create 3D scatter plot with improved voxel representation
|
46 |
+
fig = go.Figure(data=[go.Scatter3d(
|
47 |
+
x=x, y=y, z=z,
|
48 |
+
mode='markers',
|
49 |
+
marker=dict(
|
50 |
+
size=6, # Reduced size for better visualization
|
51 |
+
color=rgb_colors,
|
52 |
+
opacity=1.0,
|
53 |
+
symbol='square', # Using square symbol (supported by Plotly)
|
54 |
+
line=dict(width=0), # Remove border lines completely
|
55 |
+
sizemode='diameter', # Fixed size regardless of zoom
|
56 |
+
sizeref=1.0 # Reference scale for consistent size
|
57 |
+
)
|
58 |
+
)])
|
59 |
+
|
60 |
+
# Calculate center and range for better camera positioning
|
61 |
+
center_x = (x.max() + x.min()) / 2
|
62 |
+
center_y = (y.max() + y.min()) / 2
|
63 |
+
center_z = (z.max() + z.min()) / 2
|
64 |
+
max_range = max(x.max() - x.min(), y.max() - y.min(), z.max() - z.min())
|
65 |
+
|
66 |
+
# Update layout for better visualization
|
67 |
+
fig.update_layout(
|
68 |
+
scene=dict(
|
69 |
+
aspectmode='cube', # Force cubic aspect ratio
|
70 |
+
camera=dict(
|
71 |
+
up=dict(x=0, y=1, z=0),
|
72 |
+
center=dict(x=0, y=0, z=0),
|
73 |
+
eye=dict(x=1.5, y=0.9, z=0.9) # Adjusted camera position for a closer view while maintaining angle
|
74 |
+
),
|
75 |
+
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)'),
|
76 |
+
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)'),
|
77 |
+
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)')
|
78 |
+
),
|
79 |
+
margin=dict(l=0, r=0, t=0, b=0),
|
80 |
+
showlegend=False,
|
81 |
+
template='plotly_dark', # Dark theme for better contrast
|
82 |
+
paper_bgcolor='rgba(0,0,0,1)', # Pure black background
|
83 |
+
plot_bgcolor='rgba(0,0,0,1)'
|
84 |
+
)
|
85 |
+
|
86 |
+
return fig
|
87 |
+
|
88 |
+
def display_vox_model(vox_file):
|
89 |
+
"""Main function to display the voxel model"""
|
90 |
+
try:
|
91 |
+
# Load the vox model
|
92 |
+
if not vox_file:
|
93 |
+
return "Please upload a .vox file"
|
94 |
+
|
95 |
+
if not vox_file.name.endswith('.vox'):
|
96 |
+
return "Please upload a valid .vox file"
|
97 |
+
|
98 |
+
print(f"Loading vox file: {vox_file.name}")
|
99 |
+
voxels, palette = load_vox_model(vox_file.name)
|
100 |
+
|
101 |
+
if voxels is None or palette is None:
|
102 |
+
return "Error: Could not load voxel data from file"
|
103 |
+
|
104 |
+
if voxels.size == 0:
|
105 |
+
return "Error: No voxels found in the model"
|
106 |
+
|
107 |
+
print(f"Model loaded successfully. Shape: {voxels.shape}, Palette size: {len(palette)}")
|
108 |
+
|
109 |
+
# Create the 3D visualization
|
110 |
+
fig = create_3d_scatter(voxels, palette)
|
111 |
+
|
112 |
+
return fig
|
113 |
+
except Exception as e:
|
114 |
+
print(f"Error details: {str(e)}")
|
115 |
+
return f"Error loading model: {str(e)}"
|
116 |
+
|
117 |
+
# Create Gradio interface
|
118 |
+
interface = gr.Interface(
|
119 |
+
fn=display_vox_model,
|
120 |
+
inputs=gr.File(label="Upload .vox file"),
|
121 |
+
outputs=gr.Plot(label="3D Voxel Model"),
|
122 |
+
title="Voxel Model Viewer",
|
123 |
+
description="Upload a .vox file to view the 3D voxelized model.",
|
124 |
+
examples=[["modelo_optimizado.vox"]]
|
125 |
+
)
|
126 |
+
|
127 |
+
if __name__ == "__main__":
|
128 |
+
interface.launch()
|