Martin MX Interactive Map

Toggle Layers

Click to Activate 3D Model

Tap here to enable 3D interaction

🎯

Embed Codes

Header Video Embed Code

<video src="https://a3edrone.com/Videos/MartinMX/MartinMX.mp4" autoplay loop muted playsinline style="width:100%;height:auto;object-fit:cover;border-radius:8px;"></video>

UI & 3D Model Embed Code

<!-- Martin MX Interactive 3D Model Full Embed -->
<style>
.model-section { display: flex; flex-direction: column; align-items: center; gap: 40px; }
.layer-controls { display: flex; flex-direction: row; align-items: center; justify-content: center; background: #000; border: 2px solid #00ff00; border-radius: 8px; padding: 12px 24px; margin-bottom: 20px; }
.layer-controls ul { display: flex; flex-wrap: wrap; gap: 16px; list-style: none; padding: 0; margin: 0; }
.layer-controls li { margin-bottom: 0; white-space: nowrap; }
.layer-controls label { font-size: 14px; cursor: pointer; display: flex; align-items: center; gap: 6px; color: #fff; font-weight: 500; }
.layer-controls input[type="checkbox"] { appearance: none; width: 16px; height: 16px; margin: 0; cursor: pointer; border: 2px solid #fff; background: #000; border-radius: 3px; position: relative; transition: all 0.2s; }
.layer-controls input[type="checkbox"]:checked { background: #00ff00; border: 2px solid #000; }
.layer-controls input[type="checkbox"]:checked::after { content: '✓'; position: absolute; top: -1px; left: 2px; color: #000; font-size: 12px; font-weight: bold; line-height: 1; }
.layer-controls input[type="checkbox"]:hover { transform: scale(1.1); box-shadow: 0 0 5px rgba(0,255,0,0.5); }
.vertical-divider { width: 2px; height: 40px; background: #00ff00; margin: 0 20px; align-self: center; }
.toggle-all-section { display: flex; align-items: center; justify-content: center; padding: 0; }
.toggle-all-button { background: transparent; border: 2px solid #fff; color: #fff; font-size: 14px; font-weight: 500; padding: 8px 16px; border-radius: 4px; cursor: pointer; transition: all 0.3s; min-width: 80px; }
.toggle-all-button.all-on { color: #00ff00; border-color: #00ff00; box-shadow: 0 0 5px rgba(0,255,0,0.3); }
.toggle-all-button.mixed { color: #888; border-color: #666; opacity: 0.7; }
.sketchfab-embed-wrapper { width: 100%; max-width: 900px; min-width: 320px; aspect-ratio: 16/9; position: relative; background: #222; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 24px #0002; border: 2px solid #fff !important; }
.sketchfab-embed-wrapper iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; pointer-events: none; transition: pointer-events 0.3s ease; }
.sketchfab-embed-wrapper.active iframe { pointer-events: auto; }
.model-activation-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: transparent; display: flex; align-items: center; justify-content: center; z-index: 10; cursor: pointer; transition: opacity 0.3s ease; }
.model-activation-overlay.hidden { opacity: 0; pointer-events: none; }
.activation-message { display: none; }
@media (min-width: 900px) and (max-width: 2000px) { .layer-controls { flex-direction: column; flex-wrap: wrap; max-width: 95vw; padding: 16px 28px; justify-content: center; gap: 12px; } .toggle-all-section { order: -1; margin: 0 0 8px 0; align-self: center; } .layer-controls > *:not(.toggle-all-section) { order: 1; } .layer-controls ul { flex-wrap: wrap; justify-content: center; gap: 14px; max-width: 100%; } .layer-controls li { flex: 0 0 auto; } .layer-controls label { font-size: 13px; white-space: nowrap; } .vertical-divider { display: none; } .sketchfab-embed-wrapper { max-width: 95vw; width: 100%; } }
@media (max-width: 900px) { .sketchfab-embed-wrapper, .layer-controls { max-width: 100%; width: 100%; } }
@media (max-width: 600px) {
  .hide-video-btn { display: none !important; }
  .sketchfab-embed-wrapper { width: 90vw !important; min-width: 0 !important; max-width: 90vw !important; height: 126vw !important; aspect-ratio: unset !important; border-radius: 8px; padding-bottom: 0 !important; position: relative; border: 2px solid #fff !important; }
  .sketchfab-embed-wrapper iframe { width: 90vw !important; height: 126vw !important; min-height: 0 !important; max-height: none !important; position: absolute; top: 0; left: 0; }
}
</style>
<div class="model-section">
  <div class="layer-controls">
    <ul>
      <li><label><input type="checkbox" data-layer="Main Entrance">Main Entrance</label></li>
      <li><label><input type="checkbox" data-layer="Fee Booth / Check-In">Fee Booth / Check-In</label></li>
      <li><label><input type="checkbox" data-layer="Roads">Roads</label></li>
      <li><label><input type="checkbox" data-layer="Restrooms">Restrooms</label></li>
      <li><label><input type="checkbox" data-layer="Track Entrance">Track Entrance</label></li>
      <li><label><input type="checkbox" data-layer="Track Exit">Track Exit</label></li>
      <li><label><input type="checkbox" data-layer="Finish">Finish</label></li>
      <li><label><input type="checkbox" data-layer="Junior Track">Junior Track</label></li>
      <li><label><input type="checkbox" data-layer="Track Line / Direction">Track Line / Direction</label></li>
      <li><label><input type="checkbox" data-layer="Singles">Singles</label></li>
      <li><label><input type="checkbox" data-layer="A_Doubles">Doubles</label></li>
      <li><label><input type="checkbox" data-layer="A_Triples">Triples</label></li>
      <li><label><input type="checkbox" data-layer="A_Table-Tops">Table-Tops</label></li>
      <li><label><input type="checkbox" data-layer="A_Rollers">Rollers</label></li>
    </ul>
    <div class="vertical-divider"></div>
    <div class="toggle-all-section">
      <button id="toggle-all-btn" class="toggle-all-button">All Off</button>
    </div>
  </div>
  <div class="sketchfab-embed-wrapper">
    <div class="model-activation-overlay" id="model-activation-overlay">
      <div class="activation-message"></div>
    </div>
    <iframe id="api-frame" title="Martin MX" frameborder="0" allowfullscreen mozallowfullscreen="true" webkitallowfullscreen="true" allow="autoplay; fullscreen; xr-spatial-tracking"></iframe>
  </div>
</div>
<script src="https://static.sketchfab.com/api/sketchfab-viewer-1.12.1.js"></script>
<script>
(function() {
  let apiInstance = null;
  let nodeMap = new Map();
  let nodeHierarchy = new Map();
  const modelUID = '308b91191bbc435abdfefa38b272beb4';
  setupBasicControls();
  setup3DModelActivation();
  setTimeout(function() { initializeSketchfabAPI(); }, 500);
  function setup3DModelActivation() {
    const overlay = document.getElementById('model-activation-overlay');
    const wrapper = document.querySelector('.sketchfab-embed-wrapper');
    if (!overlay || !wrapper) return;
    overlay.addEventListener('click', function() {
      overlay.classList.add('hidden');
      wrapper.classList.add('active');
      setTimeout(function() {
        if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
      }, 300);
    });
  }
  function setupBasicControls() {
    const checkboxes = document.querySelectorAll('.layer-controls input[type="checkbox"]');
    const toggleAllBtn = document.getElementById('toggle-all-btn');
    checkboxes.forEach((checkbox) => {
      const layerName = checkbox.getAttribute('data-layer');
      checkbox.checked = false;
      checkbox.disabled = false;
      checkbox.addEventListener('change', function(e) {
        setCollectionVisibility(layerName, e.target.checked);
        updateToggleAllButton();
      });
    });
    toggleAllBtn.addEventListener('click', function() {
      const checkboxes = document.querySelectorAll('.layer-controls input[type="checkbox"]:not(:disabled)');
      const checkedBoxes = document.querySelectorAll('.layer-controls input[type="checkbox"]:checked:not(:disabled)');
      let shouldTurnOn = checkedBoxes.length === 0;
      checkboxes.forEach((checkbox) => {
        checkbox.checked = shouldTurnOn;
        setCollectionVisibility(checkbox.getAttribute('data-layer'), shouldTurnOn);
      });
      updateToggleAllButton();
    });
    updateToggleAllButton();
  }
  function updateToggleAllButton() {
    const toggleAllBtn = document.getElementById('toggle-all-btn');
    const checkboxes = document.querySelectorAll('.layer-controls input[type="checkbox"]:not(:disabled)');
    const checkedBoxes = document.querySelectorAll('.layer-controls input[type="checkbox"]:checked:not(:disabled)');
    toggleAllBtn.classList.remove('all-on', 'mixed');
    if (checkedBoxes.length === 0) {
      toggleAllBtn.textContent = 'All On';
    } else if (checkedBoxes.length === checkboxes.length) {
      toggleAllBtn.textContent = 'All Off';
      toggleAllBtn.classList.add('all-on');
    } else {
      toggleAllBtn.textContent = 'All Off';
      toggleAllBtn.classList.add('mixed');
    }
  }
  function initializeSketchfabAPI() {
    const iframe = document.getElementById('api-frame');
    if (!iframe || typeof Sketchfab === 'undefined') return;
    const client = new Sketchfab('1.12.1', iframe);
    client.init(modelUID, {
      success: function(api) {
        apiInstance = api;
        api.start();
        buildNodeHierarchy();
        api.addEventListener('viewerready', function() {
          setTimeout(function() { setupLayerControl(api); }, 1200);
        });
      },
      error: function() { alert('Sketchfab API failed to load.'); },
      ui_stop: 0, ui_inspector: 0, ui_watermark: 0, ui_controls: 1, ui_infos: 0, ui_settings: 0, transparent: 1, autostart: 1, camera: 0, ui_animations: 0, ui_annotations: 0, ui_loading: 0
    });
  }
  function buildNodeHierarchy() {
    const hierarchyData = {
        3:[0,1,2], // Always on: Collection
        79:[76,77,78], // Main Entrance
        83:[80,81,82], // Fee Booth / Check-In
        75:[73,74], // Roads
        101:[90,91,92,93,94,95,96,97,98,99,100], // Restrooms
        87:[86], // Track Entrance
        89:[88], // Track Exit
        85:[84], // Finish
        106:[102,103,104,105], // Junior Track
        143:[107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142], // Track Line / Direction
        72:[68,69,70,71], // Singles
        67:[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], // Doubles
        25:[13,14,15,16,17,18,19,20,21,22,23,24], // Triples
        12:[4,5,6,7,8,9,10,11], // Table-Tops
        30:[26,27,28,29] // Rollers
    };
    for (const [parentId, children] of Object.entries(hierarchyData)) {
        nodeHierarchy.set(parseInt(parentId), children);
    }
  }
  function setupLayerControl(api) {
    api.getNodeMap(function(err, nodes) {
      if (err) return;
      Object.entries(nodes).forEach(([instanceID, node]) => {
        const nodeId = parseInt(instanceID);
        const nodeName = node.name || `Node_${nodeId}`;
        if (!nodeMap.has(nodeName)) nodeMap.set(nodeName, []);
        nodeMap.get(nodeName).push(nodeId);
      });
      // Hide all layers initially
      ["Main Entrance","Fee Booth / Check-In","Roads","Restrooms","Track Entrance","Track Exit","Finish","Junior Track","Track Line / Direction","Singles","Doubles","Triples","Table-Tops","Rollers"].forEach(name => setCollectionVisibility(name, false));
    });
  }
  function setCollectionVisibility(collectionName, visible) {
    // Map UI label 'Roads' to collection 'Road'
    let actualCollectionName = collectionName;
    if (collectionName === 'Roads') {
      actualCollectionName = 'Road';
    }
    if (!apiInstance) return;
    const processedNodes = new Set();
    const matchingNodes = [];
    if (nodeMap.has(actualCollectionName)) matchingNodes.push(...nodeMap.get(actualCollectionName));
    for (const [nodeName, nodeIds] of nodeMap.entries()) {
      if (nodeName !== actualCollectionName && (nodeName.includes(actualCollectionName) || actualCollectionName.includes(nodeName))) {
        matchingNodes.push(...nodeIds);
      }
    }
    matchingNodes.forEach(nodeId => setNodeVisibilityRecursive(nodeId, visible, processedNodes));
  }
  function setNodeVisibilityRecursive(nodeId, visible, processedNodes) {
    if (processedNodes.has(nodeId)) return;
    processedNodes.add(nodeId);
    try {
      if (visible) apiInstance.show(nodeId); else apiInstance.hide(nodeId);
      const children = nodeHierarchy.get(nodeId) || [];
      children.forEach(childId => setNodeVisibilityRecursive(childId, visible, processedNodes));
    } catch (e) {}
  }
})();
</script>