|
<!DOCTYPE html> |
|
<html> |
|
|
|
<head> |
|
|
|
<script src="https://cdn.jsdelivr.net/npm/@google/model-viewer@3.1.1/dist/model-viewer.min.js" |
|
type="module"></script> |
|
<style> |
|
body { |
|
margin: 0; |
|
font-family: Arial, sans-serif; |
|
} |
|
|
|
.centered-container { |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
} |
|
|
|
.modelviewer-panel-button { |
|
height: 30px; |
|
margin: 4px 4px; |
|
padding: 0px 14px; |
|
background: white; |
|
border-radius: 10px; |
|
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25); |
|
font-size: 14px; |
|
font-weight: 600; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
cursor: pointer; |
|
transition: all 0.2s ease; |
|
} |
|
|
|
.modelviewer-panel-button.checked { |
|
background: #6567C9; |
|
color: white; |
|
} |
|
|
|
.modelviewer-panel-button:hover { |
|
background-color: #e2e6ea; |
|
} |
|
|
|
.modelviewer-panel-button-container { |
|
display: flex; |
|
justify-content: space-around; |
|
} |
|
|
|
.centered-container { |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
} |
|
|
|
</style> |
|
</head> |
|
|
|
<body> |
|
<div class="centered-container"> |
|
<div class="centered-container"> |
|
<div class="column is-mobile is-centered"> |
|
<model-viewer id="modelviewer" style="height: #height#px; width: #width#px;" |
|
rotation-per-second="10deg" |
|
src="#src#" disable-tap |
|
environment-image="neutral" |
|
camera-target="0m 0m 0m" |
|
camera-orbit="0deg 90deg 12m" |
|
orientation="0deg 0deg 0deg" |
|
shadow-intensity=".9" |
|
ar auto-rotate |
|
camera-controls> |
|
</model-viewer> |
|
</div> |
|
|
|
<div class="modelviewer-panel-button-container"> |
|
<div id="appearance-button" class="modelviewer-panel-button small checked" onclick="showTexture()"> |
|
Appearance |
|
</div> |
|
<div id="geometry-button" class="modelviewer-panel-button small" onclick="hideTexture()">Geometry</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
let modelViewer; |
|
let window_state = { |
|
isModelLoaded: false, |
|
isHidden: false, |
|
savedMaterials: null, |
|
savedExposure: null |
|
}; |
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
modelViewer = document.getElementById('modelviewer'); |
|
|
|
|
|
modelViewer.addEventListener('load', () => { |
|
console.log('Model loaded, materials available:', modelViewer.model.materials.length); |
|
window_state.isModelLoaded = true; |
|
|
|
|
|
modelViewer.model.materials.forEach((mat, idx) => { |
|
console.log(`Material ${idx}:`, { |
|
name: mat.name, |
|
hasBaseColor: !!mat.pbrMetallicRoughness.baseColorTexture, |
|
hasMetallicRoughness: !!mat.pbrMetallicRoughness.metallicRoughnessTexture, |
|
hasNormal: !!mat.normalTexture |
|
}); |
|
}); |
|
}); |
|
|
|
modelViewer.addEventListener('error', (event) => { |
|
console.error('Model loading error:', event); |
|
}); |
|
}); |
|
|
|
function ensureModelLoaded() { |
|
if (!window_state.isModelLoaded || !modelViewer.model || !modelViewer.model.materials) { |
|
console.warn('Model not loaded yet'); |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
function saveMaterialsState() { |
|
if (!ensureModelLoaded()) return false; |
|
|
|
console.log('Saving materials state...'); |
|
window_state.savedMaterials = []; |
|
window_state.savedExposure = modelViewer.exposure; |
|
|
|
for (let i = 0; i < modelViewer.model.materials.length; i++) { |
|
const material = modelViewer.model.materials[i]; |
|
const pbr = material.pbrMetallicRoughness; |
|
|
|
const materialState = { |
|
baseColorTexture: null, |
|
metallicRoughnessTexture: null, |
|
normalTexture: null, |
|
baseColorFactor: null, |
|
metallicFactor: null, |
|
roughnessFactor: null |
|
}; |
|
|
|
|
|
try { |
|
if (pbr.baseColorTexture && pbr.baseColorTexture.texture) { |
|
materialState.baseColorTexture = pbr.baseColorTexture.texture; |
|
console.log(`Saved baseColorTexture for material ${i}`); |
|
} |
|
if (pbr.metallicRoughnessTexture && pbr.metallicRoughnessTexture.texture) { |
|
materialState.metallicRoughnessTexture = pbr.metallicRoughnessTexture.texture; |
|
console.log(`Saved metallicRoughnessTexture for material ${i}`); |
|
} |
|
if (material.normalTexture && material.normalTexture.texture) { |
|
materialState.normalTexture = material.normalTexture.texture; |
|
console.log(`Saved normalTexture for material ${i}`); |
|
} |
|
|
|
|
|
materialState.baseColorFactor = [...pbr.baseColorFactor]; |
|
materialState.metallicFactor = pbr.metallicFactor; |
|
materialState.roughnessFactor = pbr.roughnessFactor; |
|
|
|
} catch (error) { |
|
console.error(`Error saving material ${i}:`, error); |
|
} |
|
|
|
window_state.savedMaterials.push(materialState); |
|
} |
|
|
|
console.log('Materials state saved:', window_state.savedMaterials); |
|
return true; |
|
} |
|
|
|
function hideTexture() { |
|
console.log('hideTexture called'); |
|
|
|
if (!ensureModelLoaded()) { |
|
console.error('Cannot hide texture: model not loaded'); |
|
return; |
|
} |
|
|
|
let appearanceButton = document.getElementById('appearance-button'); |
|
let geometryButton = document.getElementById('geometry-button'); |
|
appearanceButton.classList.remove('checked'); |
|
geometryButton.classList.add('checked'); |
|
|
|
|
|
if (window_state.isHidden) return; |
|
|
|
|
|
if (!window_state.savedMaterials) { |
|
if (!saveMaterialsState()) return; |
|
} |
|
|
|
|
|
try { |
|
for (let i = 0; i < modelViewer.model.materials.length; i++) { |
|
const material = modelViewer.model.materials[i]; |
|
const pbr = material.pbrMetallicRoughness; |
|
|
|
if (pbr.baseColorTexture) { |
|
pbr.baseColorTexture.setTexture(null); |
|
} |
|
if (pbr.metallicRoughnessTexture) { |
|
pbr.metallicRoughnessTexture.setTexture(null); |
|
} |
|
if (material.normalTexture) { |
|
material.normalTexture.setTexture(null); |
|
} |
|
} |
|
|
|
window_state.isHidden = true; |
|
modelViewer.environmentImage = '/static/env_maps/gradient.jpg'; |
|
modelViewer.exposure = 4; |
|
|
|
console.log('Textures hidden successfully'); |
|
} catch (error) { |
|
console.error('Error hiding textures:', error); |
|
} |
|
} |
|
|
|
function showTexture() { |
|
console.log('showTexture called'); |
|
|
|
if (!ensureModelLoaded()) { |
|
console.error('Cannot show texture: model not loaded'); |
|
return; |
|
} |
|
|
|
let appearanceButton = document.getElementById('appearance-button'); |
|
let geometryButton = document.getElementById('geometry-button'); |
|
appearanceButton.classList.add('checked'); |
|
geometryButton.classList.remove('checked'); |
|
|
|
|
|
if (!window_state.isHidden) return; |
|
|
|
|
|
if (!window_state.savedMaterials) { |
|
console.warn('No saved materials to restore'); |
|
return; |
|
} |
|
|
|
|
|
try { |
|
for (let i = 0; i < modelViewer.model.materials.length && i < window_state.savedMaterials.length; i++) { |
|
const material = modelViewer.model.materials[i]; |
|
const pbr = material.pbrMetallicRoughness; |
|
const savedMaterial = window_state.savedMaterials[i]; |
|
|
|
|
|
if (savedMaterial.baseColorTexture && pbr.baseColorTexture) { |
|
pbr.baseColorTexture.setTexture(savedMaterial.baseColorTexture); |
|
console.log(`Restored baseColorTexture for material ${i}`); |
|
} |
|
if (savedMaterial.metallicRoughnessTexture && pbr.metallicRoughnessTexture) { |
|
pbr.metallicRoughnessTexture.setTexture(savedMaterial.metallicRoughnessTexture); |
|
console.log(`Restored metallicRoughnessTexture for material ${i}`); |
|
} |
|
if (savedMaterial.normalTexture && material.normalTexture) { |
|
material.normalTexture.setTexture(savedMaterial.normalTexture); |
|
console.log(`Restored normalTexture for material ${i}`); |
|
} |
|
|
|
|
|
if (savedMaterial.baseColorFactor) { |
|
pbr.setBaseColorFactor(savedMaterial.baseColorFactor); |
|
} |
|
if (typeof savedMaterial.metallicFactor === 'number') { |
|
pbr.setMetallicFactor(savedMaterial.metallicFactor); |
|
} |
|
if (typeof savedMaterial.roughnessFactor === 'number') { |
|
pbr.setRoughnessFactor(savedMaterial.roughnessFactor); |
|
} |
|
} |
|
|
|
|
|
modelViewer.environmentImage = '/static/env_maps/white.jpg'; |
|
if (window_state.savedExposure !== undefined) { |
|
modelViewer.exposure = window_state.savedExposure; |
|
} |
|
|
|
window_state.isHidden = false; |
|
console.log('Textures restored successfully'); |
|
|
|
} catch (error) { |
|
console.error('Error restoring textures:', error); |
|
} |
|
} |
|
|
|
|
|
function debugMaterials() { |
|
if (!ensureModelLoaded()) return; |
|
|
|
console.log('=== Current Materials Debug ==='); |
|
modelViewer.model.materials.forEach((mat, idx) => { |
|
const pbr = mat.pbrMetallicRoughness; |
|
console.log(`Material ${idx}:`, { |
|
name: mat.name, |
|
baseColorTexture: pbr.baseColorTexture?.texture || null, |
|
metallicRoughnessTexture: pbr.metallicRoughnessTexture?.texture || null, |
|
normalTexture: mat.normalTexture?.texture || null, |
|
baseColorFactor: pbr.baseColorFactor, |
|
metallicFactor: pbr.metallicFactor, |
|
roughnessFactor: pbr.roughnessFactor |
|
}); |
|
}); |
|
console.log('Window state:', window_state); |
|
console.log('==============================='); |
|
} |
|
|
|
|
|
window.debugMaterials = debugMaterials; |
|
|
|
</script> |
|
</body> |
|
|
|
</html> |