Py.Cafe

jackparmer/

voxel-dashboard

Voxel World Generator Demo using Dash

DocsPricing
  • app.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
from dash import Dash, Output, Input, html, dcc, callback
import base64
from PIL import Image
import vnoise
import numpy as np
import random
from voxel_world import Volume

app = Dash(__name__)

# Using base64 encoding and decoding
def b64_image(image_bytes):
    return 'data:image/png;base64,' + base64.b64encode(image_bytes).decode('utf-8')

style=dict(background='black', color='white', fontFamily='sans-serif', fontSize='12px')

# Layout
app.layout = html.Div(style=style, children=[
    html.Div([
        html.Label('Theme'),
        dcc.Dropdown(
            id='theme-dropdown',
            options=[{'label': theme, 'value': theme} for theme in Volume.themes.keys()],
            value='Lilac'
        ),
        html.Label('Resolution'),
        dcc.Slider(id='resolution-slider', min=1, max=20, step=1, value=10, marks={i: str(i) for i in range(1, 21)[0::5]}),
        html.Label('Viewing Angle X'),
        dcc.Slider(id='angle-x-slider', min=0, max=90, step=1, value=45, marks={i: str(i) for i in range(0, 91, 10)[0::10]}),
        html.Label('Viewing Angle Y'),
        dcc.Slider(id='angle-y-slider', min=0, max=90, step=1, value=30, marks={i: str(i) for i in range(0, 91, 10)[0::10]}),
        html.Label('Zoom'),
        dcc.Slider(id='zoom-slider', min=0.1, max=5.0, step=0.1, value=2.0, marks={i/10: str(i/10) for i in range(1, 51, 5)[0::10]}),
        html.Label('Voxel Color'),
        dcc.Input(id='voxel-color', type='color', value='#FFFFFF'),
        html.Label('Specularity'),
        dcc.Slider(id='specularity-slider', min=0, max=1, step=0.01, value=0.5, marks={i/10: str(i/10) for i in range(0, 11)[0::2]}),
        html.Label('Perlin Noise Scale'),
        dcc.Slider(id='noise-scale-slider', min=1, max=50, step=1, value=10, marks={i: str(i) for i in range(1, 51, 5)[0::5]}),
        html.Label('Perlin Noise Octaves'),
        dcc.Slider(id='noise-octaves-slider', min=1, max=10, step=1, value=4, marks={i: str(i) for i in range(1, 11)[0::2]}),
        html.Label('Perlin Noise Persistence'),
        dcc.Slider(id='noise-persistence-slider', min=0.1, max=1.0, step=0.1, value=0.5, marks={i/10: str(i/10) for i in range(1, 11)[0::2]}),
        html.Label('Perlin Noise Lacunarity'),
        dcc.Slider(id='noise-lacunarity-slider', min=1.0, max=4.0, step=0.1, value=2.0, marks={i/10: str(i/10) for i in range(10, 41, 5)}),
        html.Button('Generate world', id='submit-val'),
    ], style={'width': '30%', 'display': 'inline-block', 'vertical-align': 'top'}),
    html.Div([
        html.Img(id='voxel-world', style={'width':'100%'})
    ], style={'width': '70%', 'display': 'inline-block'}),
])

# Callback
@callback(
    Output('voxel-world', 'src'),
    Input('submit-val', 'n_clicks'),
    Input('theme-dropdown', 'value'),
    Input('resolution-slider', 'value'),
    Input('angle-x-slider', 'value'),
    Input('angle-y-slider', 'value'),
    Input('zoom-slider', 'value'),
    Input('voxel-color', 'value'),
    Input('specularity-slider', 'value'),
    Input('noise-scale-slider', 'value'),
    Input('noise-octaves-slider', 'value'),
    Input('noise-persistence-slider', 'value'),
    Input('noise-lacunarity-slider', 'value'),
)
def update_output(n_clicks, theme, resolution, angle_x, angle_y, zoom, voxel_color, specularity, noise_scale, noise_octaves, noise_persistence, noise_lacunarity):
    """ Generate voxel world """
    noise = vnoise.Noise()
    size = 16
    voxel_matrix = np.array([[[1 if noise.noise3(x / noise_scale, y / noise_scale, z / noise_scale) > random.uniform(-0.2, 0.2) else 0 for z in range(size)] for y in range(size)] for x in range(size)], dtype=np.uint8)

    # Create color matrix with the selected voxel color
    r, g, b = tuple(int(voxel_color[i:i+2], 16) for i in (1, 3, 5))
    color_matrix = np.zeros((size, size, size, 3), dtype=np.uint8)
    color_matrix[voxel_matrix > 0] = [r, g, b]

    # Create specularity matrix
    specularity_matrix = np.full((size, size, size), specularity, dtype=np.float32)

    img_bytes = Volume(
        voxel_matrix,
        theme=theme,
        resolution=resolution,
        viewing_angle=(angle_x, angle_y),
        zoom=zoom,
        dark_bg=True,
        color_matrix=color_matrix,
        specularity_matrix=specularity_matrix
    ).byte_stream().getvalue()

    return b64_image(img_bytes)

if __name__ == '__main__':
    app.run(debug=True)