Skip to content

veridock/svg3d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 

Repository files navigation

svg3d - SVG-packed Universal 3D Viewer

Rozszerzenie pliku: .svg Cel: pojedynczy plik SVG zawierający UI (HTML) i logikę (JavaScript) osadzone w <foreignObject>, który w przeglądarce ładuje i renderuje modele 3D (glTF/GLB, OBJ, STL, DAE/Collada itp.) przy użyciu WebGL/Three.js.

  • Plik SVG zawiera element <foreignObject> z wewnętrznym dokumentem XHTML (np. <body xmlns="http://www.w3.org/1999/xhtml">) — wewnątrz: HTML + <canvas> + <script>.

  • JavaScript ładuje biblioteki (np. Three.js, GLTFLoader) i renderuje scenę w <canvas>.

  • Problem, który zgłosiłeś: błąd przetwarzania XML — powodem są niezabezpieczone fragmenty JavaScript (np. // komentarze, </script> wewnątrz kodu, znaki < &) które łamią reguły XML. Rozwiązaniem jest jedno z poniższych:

    • umieszczenie kodu JS w sekcji CDATA: <![CDATA[ ... ]]>
    • albo trzymanie logiki w zewnętrznym skrypcie i wstawienie tylko <script src="..."></script> (zalecane dla większych projektów)
    • albo uciekanie (escaping) znaków problematycznych, ALE to jest uciążliwe i łatwo popełnić błąd.

2. Schemat pliku (wysokopoziomowo)

<svg ...>
  <defs> ... opcjonalne definicje ... </defs>
  <rect .../>  <!-- tło etc. -->
  <foreignObject x=... y=... width=... height=...>
    <body xmlns="http://www.w3.org/1999/xhtml">
      <!-- UI: inputs, canvas -->
      <canvas id="canvas"></canvas>

      <!-- Sposób A: inline script - NAJWAŻNIEJSZE: zawrzyj JS w CDATA -->
      <script type="application/javascript"><![CDATA[
        // Twój JS (możesz użyć import(...) dynam., albo tworzyć <script type="module"> dynamicznie)
      ]]></script>

      <!-- Sposób B (zalecany) : odwołanie do zewnętrznego pliku JS -->
      <script type="module" src="viewer-module.js"></script>
    </body>
  </foreignObject>
</svg>

3. Dlaczego pojawił się błąd XML? (konkretnie)

XML (w tym plik SVG jako XML) wymaga, aby dokument był well-formed. Wewnątrz XML:

  • // komentarz w skrypcie sam w sobie nie jest problemem — problem robią znaki </script> w stringach lub niezamknięte nawiasy, a przede wszystkim znaki < i & w treści, które XML interpretuje jako znaczniki/encje.
  • Jeśli używasz XHTML wewnątrz <foreignObject>, to zawartość musi być poprawnym XML, więc zwykły JS (zwłaszcza z </script> fragmentami) może złamać parser.
  • Najpewniejsze rozwiązanie: CDATA lub zewnętrzny skrypt.

4. Rekomendacje praktyczne

  1. Dla prostych projektów / single-file: użyj <![CDATA[ ... ]]> otaczającego cały kod JS (również importów).

    • Uwaga: <![CDATA[ zabrania interpretacji < i & — więc XML parser nie narzeka.
  2. Dla większych projektów: trzymaj skrypty w zewnętrznym pliku .js i odwołuj się przez <script type="module" src="..."></script> (w obrębie foreignObject).

    • Jeśli chcesz mieć naprawdę pojedynczy plik (self-contained), możesz dołączyć skrypt zewnętrzny jako base64 w src (data URL) lub wstawić go jako CDATA.
  3. Unikaj umieszczania znaków </script> w stringach JS — lepiej trzymać w CDATA.

  4. Testowanie: otwieraj plik przez prosty server (python -m http.server) aby uniknąć niektórych ograniczeń przeglądarki przy file://.


5. Poprawiony, minimalny przykład SVG (skrócony) — używający CDATA

Uwaga: ten przykład pokazuje sposób zabezpieczenia przed błędami XML; to nie pełna wersja Three.js, ale pokazuje strukturę i poprawne umieszczenie skryptu.

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" viewBox="0 0 800 600">
  <rect width="100%" height="100%" fill="#111"/>
  <foreignObject x="10" y="10" width="780" height="580">
    <body xmlns="http://www.w3.org/1999/xhtml" style="margin:0">
      <div style="width:100%;height:100%;display:flex;flex-direction:column">
        <canvas id="canvas" style="flex:1; width:100%; height:100%"></canvas>

        <!-- Inline JS — BEZPIECZNE: wszystko w CDATA -->
        <script type="application/javascript"><![CDATA[
          // kod JS tutaj — możesz używać "//" i "<" bez łamania XML
          (function(){
            const canvas = document.getElementById('canvas');
            // prosty rysunek testowy
            const ctx = canvas.getContext('2d');
            canvas.width = canvas.clientWidth;
            canvas.height = canvas.clientHeight;
            ctx.fillStyle = '#223';
            ctx.fillRect(0,0,canvas.width,canvas.height);
            ctx.fillStyle = '#fff';
            ctx.fillText('SVG-packed viewer (minimal test)', 20, 40);
            // W praktyce: tutaj import i inicjacja Three.js (najlepiej jako dynamiczny moduł)
          })();
        ]]></script>
      </div>
    </body>
  </foreignObject>
</svg>

6. Jak bezpiecznie używać modułów ES (type="module") wewnątrz SVG?

Przeglądarki mogą mieć różne zachowanie przy type="module" wewnątrz <foreignObject> i CDATA. Możliwości:

  • Zewnętrzny modul: <script type="module" src="viewer-module.js"></script> — najprostsze i najmniej kłopotliwe.
  • Inline module w CDATA: <script type="module"><![CDATA[ import ...; ... ]]></script> — zadziała w większości przeglądarek, ale testuj.
  • Tworzenie dynamicznego modułu: w prostym <script> (classic) w CDATA stwórz element <script type="module"> dynamicznie i ustaw jego textContent na treść modułu (to obejście, gdy inline module bezpośrednio nie działa).

Przykład dynamicznego wstrzyknięcia modułu (do użycia w CDATA):

const moduleCode = `import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js';
// ... reszta modułu ...`;
const s = document.createElement('script');
s.type = 'module';
s.textContent = moduleCode;
document.body.appendChild(s);

7. Obsługiwane formaty 3D (zalecane do przeglądarki)

  • glTF / GLB (najlepszy wybór dla WebGL; PBR, animacje, compact) — zalecane .glb dla single-file.
  • OBJ (geometry + MTL, bez PBR) — prosty, szeroko obsługiwany.
  • STL (ASCII/bin) — do geometrii, bez materiałów.
  • COLLADA (.dae) — xml-owy; używany dawniej, ale działa.
  • USDZ — działa głównie w Safari/AR Quick Look, nie jest bezpośrednio renderowany przez Three.js.
  • X3D — możliwy, ale wymaga odrębnego silnika (x3dom, X3DOM).

8. Embedding modelu (base64 / data URI)

Aby mieć w pełni self-contained plik z modelem GLB, możesz zembedować model jako data URL:

// przykładowy URL: 'data:model/gltf-binary;base64,AAAA...'
gltfLoader.load('data:model/gltf-binary;base64,....', (gltf) => { ... });

Uwaga:

  • Plik SVG stanie się bardzo duży (rozmiar base64 ≈ 4/3 rzeczywistego rozmiaru binarnego).
  • Niektóre przeglądarki mogą mieć limit długości URL — testuj przy dużych plikach.

Alternatywa: wstawić binarny model jako <script type="application/octet-stream" id="model-data"> albo jako element <metadata> z CDATA i potem parsować. To działa, lecz jest mniej standardowe.


9. CORS i serwery

  • Jeśli ładujesz model z innego hosta przez URL (fetch lub loader), serwer musi zwracać nagłówek Access-Control-Allow-Origin: * lub origin twojej strony. Brak tego headera → błędy w przeglądarce.
  • Przy lokalnym testowaniu używaj prostego serwera (python -m http.server albo npx http-server) zamiast file://, by uniknąć problemów z modułami i CORS.

10. Bezpieczeństwo

  • Plik SVG może zawierać skrypt — uruchamiaj tylko zaufane pliki. Nie otwieraj SVG z nieznanego źródła (może zawierać złośliwy JS).

  • Jeśli dystrybuujesz taki viewer publicznie, rozważ:

    • ograniczenie funkcji (np. brak dostępu do sieci lokalnej),
    • Content Security Policy (CSP) na serwerze hostującym (jeśli używasz HTML wrappera, nie samego SVG),
    • nie używaj inline scriptów w środowisku High-Security bez audytu.

11. Diagnostyka najczęstszych błędów

  • „Błąd przetwarzania XML: nieprawidłowo sformowany” → zwykle brak CDATA lub obecność </script> w tekście. Rozwiązanie: użyj CDATA lub przenieś JS do pliku.
  • CORS error on load → upewnij się, że serwer modelu zwraca Access-Control-Allow-Origin.
  • Module not found / failed to fetch → CDN może być zablokowany lub file:// nie pozwala na moduły; użyj serwera.
  • Brak widoku / czarny canvas → upewnij się, że Three.js inicjuje renderer z prawidłowym rozmiarem; wywołaj renderer.setSize() i camera.updateProjectionMatrix().

12. Przykład: kompletny fragment (Three.js + CDATA) — fragment produkcyjny

Przykład jest skrótem (pobieranie Three.js z CDN). W praktyce lepiej mieć zewnętrzny viewer-module.js. Ten fragment pokazuje w jaki sposób zabezpieczyć JS przed XML parserem.

<foreignObject x="0" y="0" width="800" height="600">
  <body xmlns="http://www.w3.org/1999/xhtml">
    <canvas id="c" style="width:100%;height:100%"></canvas>

    <script type="application/javascript"><![CDATA[
      (function(){
        // dynamiczne załadowanie modułu three.js jako modułu (tworzymy <script type="module"> dynamicznie)
        const moduleCode = `
          import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js';
          import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.160/examples/jsm/controls/OrbitControls.js';
          export function start() {
            const canvas = document.getElementById('c');
            const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
            renderer.setSize(canvas.clientWidth, canvas.clientHeight);
            const scene = new THREE.Scene();
            scene.background = new THREE.Color(0x222222);
            const camera = new THREE.PerspectiveCamera(45, canvas.clientWidth / canvas.clientHeight, 0.1, 1000);
            camera.position.set(0,1.5,3);
            const controls = new OrbitControls(camera, renderer.domElement);
            const light = new THREE.HemisphereLight(0xffffff,0x444444,1.0);
            scene.add(light);
            renderer.render(scene, camera);
            // ... dalsza logika ładowania modeli
          }
        `;
        const s = document.createElement('script');
        s.type = 'module';
        s.textContent = moduleCode + '\nimport.meta && (window.__viewer_module__ = (await (new Function(moduleCode + "\\nreturn {start};"))()).start());';
        document.body.appendChild(s);
      })();
    ]]></script>
  </body>
</foreignObject>

(Uwaga: powyższy fragment pokazuje zasadę; przy produkcji napisz moduł w pliku zewnętrznym.)


13. Dodatkowe funkcje które możesz dodać

  • DRACO decompression dla skompresowanych glTF (DRACOLoader).
  • Kompresja tekstur (KTX2 / Basis).
  • Obsługa animacji i skinned meshes.
  • Eksport/zip: możliwość pobrania modelu lub eksportu sceny.
  • Tryb AR: stream do USDZ lub WebXR support.
  • Fallback: jeśli Three.js nie jest dostępne, pokaż przyjazny komunikat.

14. Podsumowanie / najlepsze praktyki

  • Jeżeli chcesz prosty, self-contained viewer: użyj CDATA dla inline skryptów albo wbuduj model jako base64 data URL (uwaga: rozmiar).
  • Jeśli zależy ci na stabilności i czytelności: trzymaj JS jako zewnętrzny moduł i odwołuj się przez src.
  • Testuj w docelowych przeglądarkach i hostuj przez HTTP(S).
  • Dla produkcji: preferuj glTF/GLB jako format wymiany i rozglądaj się za DRACO/texture compression. yć wykonana teraz?

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published