I am currently working on an interactive UI in Jupyter Notebook based mainly on IPy Widgets to feature 3D interactive models created and exported previously using Blender. I used an HTML 3D model viewer to show the 3D model (see code below), which works perfectly fine but when I try to publish the Jupyter Notebook e.g. using Voíla it will show everything except for the 3D model (see images attached). Has anyone else encountered this issue or has an idea of how to solve it?
Any hint would be much appreciated, cheers!
JupyterNotebook before publishing with 3D model shown correctly
JupyterNotebook after publishing with Voila
The HTML model viewer code:
from IPython.display import HTML, Javascript, display
# Load the model-viewer script once
display(Javascript("""
if (!window.modelViewerLoaded) {
const script = document.createElement('script');
script.type = 'module';
script.src = 'https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js';
document.head.appendChild(script);
window.modelViewerLoaded = true;
}
"""))
# Define the model viewer display function
def display_model_waterdepth(glb_path):
return HTML(f"""
<div class="mv-wrap" style="position: relative; width: 100%; height: 375px;">
<model-viewer src="{glb_path}" alt="Waterdepth model"
interaction-prompt="auto" interaction-prompt-threshold="8000"
disable-default-lighting shadow-intensity="0" shadow-softness="0"
exposure="0.003" environment-image="neutral" camera-controls
background-color="#FDF0F5" camera-orbit="0deg 0deg 7000m"
min-camera-orbit="auto auto 1m" max-camera-orbit="auto auto 10000m"
field-of-view="45deg"
style="width: 100%; height: 100%; filter: brightness(1.0) contrast(1.3);">
</model-viewer>
<div style="position: absolute; bottom: 10px; right: 10px;
display: flex; flex-direction: column; align-items: flex-end; gap: 5px; z-index: 10;">
<button data-zoomplus style="background-color: #f8f8f8; width: 25px; height: 25px;">+</button>
<button data-zoomminus style="background-color: #f8f8f8; width: 25px; height: 25px;">-</button>
<button data-reset style="background-color: #f8f8f8; width: 25px; height: 25px;">⟳</button>
<button data-focus="1" style="background-color: #f8f8f8; width: 100px; height: 25px;">Neubacher Au</button>
<button data-focus="2" style="background-color: #f8f8f8; width: 100px; height: 25px;">Ofenloch</button>
</div>
</div>
<script>
(function() {{
const root = document.currentScript.previousElementSibling;
const viewer = root.querySelector('model-viewer');
root.querySelector('[data-zoomplus]').addEventListener('click', () => {{
const o = viewer.getCameraOrbit();
viewer.cameraOrbit = `${{o.theta}}rad ${{o.phi}}rad ${{o.radius * 0.9}}m`;
}});
root.querySelector('[data-zoomminus]').addEventListener('click', () => {{
const o = viewer.getCameraOrbit();
viewer.cameraOrbit = `${{o.theta}}rad ${{o.phi}}rad ${{o.radius * 1.1}}m`;
}});
root.querySelector('[data-reset]').addEventListener('click', () => {{
viewer.cameraOrbit = "0deg 0deg 7000m";
viewer.cameraTarget = "auto auto auto";
viewer.fieldOfView = "45deg";
viewer.jumpCameraToGoal();
}});
root.querySelectorAll('[data-focus]').forEach(btn => {{
btn.addEventListener('click', () => {{
const area = btn.getAttribute('data-focus');
if (area === "1") {{
viewer.cameraOrbit = "0deg 0deg 2300m";
viewer.cameraTarget = "-850m 0m -450m";
}} else {{
viewer.cameraOrbit = "55deg 0deg 1800m";
viewer.cameraTarget = "1000m 230m -250m";
}}
viewer.jumpCameraToGoal();
}});
}});
}})();
</script>
""")