Displaying compressed GLB/GLTF files in a Joomla module.

Hello.
I’ve created a Joomla module to display 3D GLB/GLTF models.
The module works correctly.
Now, I’d like this module to display compressed GLB/GLTF models, but I’m having trouble doing so (my JavaScript and three.js knowledge is probably insufficient). Could someone help me modify my default.php file to load the necessary DRACO and possibly KTX2 scripts?

<?php
defined('_JEXEC') or die;

use Joomla\CMS\Factory;

$document = Factory::getDocument();

$modelUrl = $params->get('model_url', '');
$backgroundImage = $params->get('background_image', '');
$backgroundColor = $params->get('background_color', 'rgba(0,0,0,0)');
$width = $params->get('width', '100%');
$height = $params->get('height', '70vh');

// Paramètres pour le titre et commentaire
$title = $params->get('title', 'Titre par défaut');
$comment = $params->get('comment', 'Commentaire par défaut');
$titleColor = $params->get('title_color', 'rgba(255,255,255,1)');
$commentColor = $params->get('comment_color', 'rgba(255,255,255,1)');
$titleFontSize = $params->get('title_font_size', '24px');
$commentFontSize = $params->get('comment_font_size', '16px');

$cameraX = (float) $params->get('camera_x', 0);
$cameraY = (float) $params->get('camera_y', 0);
$cameraZ = (float) $params->get('camera_z', 2);
$rotationX = (float) $params->get('rotation_x', 0);
$rotationY = (float) $params->get('rotation_y', 0);
$rotationZ = (float) $params->get('rotation_z', 0);

$lightIntensity = (float) $params->get('light_intensity', 1.0);
$exposure = (float) $params->get('exposure', 1.0);

$document->addCustomTag("
<script type='module'>
import * as THREE from '../../modules/mod_anwynn_3d/assets/three.module.js';
import { GLTFLoader } from '../../modules/mod_anwynn_3d/assets/GLTFLoader.js';
import { DRACOLoader } from '../../modules/mod_anwynn_3d/assets/DRACOLoader.js';
import { OrbitControls } from '../../modules/mod_anwynn_3d/assets/OrbitControls.js';

console.log('Début du script 3D');

// Initialisation de la scène et de la caméra
const container = document.getElementById('anwynn-3d-container');
const scene = new THREE.Scene();
console.log('Scène créée');

const camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);
camera.position.set({$cameraX}, {$cameraY}, {$cameraZ});
console.log('Caméra positionnée');

const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.toneMappingExposure = {$exposure};
container.appendChild(renderer.domElement);
console.log('Rendu initialisé');

" . (strpos($backgroundColor, '0)') !== false ? 'scene.background = null;' : "scene.background = new THREE.Color('{$backgroundColor}');") . "

const light = new THREE.HemisphereLight(0xffffff, 0x444444, {$lightIntensity});
scene.add(light);
console.log('Lumière ajoutée');

// Initialisation du DRACOLoader avec décodeur local
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/modules/mod_anwynn_3d/assets/draco/');
loader.setDRACOLoader(dracoLoader);
console.log('DRACOLoader configuré');

// Contrôles de la caméra
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableZoom = true;
controls.enablePan = true;
controls.enableRotate = false;
console.log('Contrôles de caméra ajoutés');

let model;
const modelGroup = new THREE.Group();
scene.add(modelGroup);
console.log('Groupe de modèles ajouté à la scène');

let isDragging = false;
let previousMousePosition = { x: 0, y: 0 };

// Gestion de l'erreur de chargement et du modèle
let isModelLoaded = false;

function loadModel(url) {
    if (isModelLoaded) {
        console.log('Le modèle a déjà été chargé.');
        return;
    }
    console.log('Début du chargement du modèle GLTF');
    loader.load(url, function (gltf) {
        console.log('Modèle GLTF chargé');
        model = gltf.scene;

        const box = new THREE.Box3().setFromObject(model);
        const center = box.getCenter(new THREE.Vector3());
        model.position.sub(center);

        model.rotation.set(
            THREE.MathUtils.degToRad({$rotationX}),
            THREE.MathUtils.degToRad({$rotationY}),
            THREE.MathUtils.degToRad({$rotationZ})
        );

        modelGroup.add(model);
        console.log('Modèle ajouté à la scène');
        animate();
        isModelLoaded = true;
    }, undefined, function (error) {
        console.error('Erreur de chargement du modèle :', error);
        alert('Erreur de chargement du modèle : ' + error.message);
    });
}

loadModel('{$modelUrl}');

renderer.domElement.addEventListener('pointerdown', (event) => {
    isDragging = true;
    previousMousePosition = {
        x: event.clientX,
        y: event.clientY
    };
});

renderer.domElement.addEventListener('pointerup', () => {
    isDragging = false;
});

renderer.domElement.addEventListener('pointermove', (event) => {
    if (!isDragging) return;

    const deltaMove = {
        x: event.clientX - previousMousePosition.x,
        y: event.clientY - previousMousePosition.y
    };

    modelGroup.rotation.y += deltaMove.x * 0.01;
    modelGroup.rotation.x += deltaMove.y * 0.01;

    previousMousePosition = {
        x: event.clientX,
        y: event.clientY
    };
});

function animate() {
    requestAnimationFrame(animate);
    controls.update();
    renderer.render(scene, camera);
    console.log('Scène rendue');
}
</script>
");

?>

<div style="margin:auto; text-align: center; color: <?php echo $titleColor; ?>; font-size: <?php echo $titleFontSize; ?>;">
    <p><?php echo $title; ?></p>
</div>

<div id="anwynn-3d-container"
     style="position: relative; width: <?php echo $width; ?>; height: <?php echo $height; ?>;
     <?php echo $backgroundImage ? 'background-image: url(' . $backgroundImage . '); background-size: cover;' : ''; ?>">
</div>

<div style="margin:auto; text-align: center; color: <?php echo $commentColor; ?>; font-size: <?php echo $commentFontSize; ?>;">
    <p><?php echo $comment; ?></p>
</div>

Do you have a console that logs an error? It’s likely the following path is incorrect, the draco directory that contains the decoder typically needs to be in the root or public directories…

Good evening and thank you for this advice.
Unfortunately, I think I’ve tried all the solutions, including this one.
As I troubleshoot the loads, the error messages multiply. glTF loader wants to be first, and when it is, it’s Draco Loader that wants to be first, and when I manage to no longer get messages from these two scripts, it’s KTX2, then finally meshopt.
I would have liked to adopt donmccurdy’s glTF viewer, but I don’t want any settings visible to the user (the settings are provided in the Joomla backend). And my JavaScript knowledge is insufficient.

It’s difficult to deduce what you mean by this, any chance you can post an image of the console logs?

Have integrated GLTF via three.js into both Joomla and Virtuemart previously.

Your best direction would to create a Joomla plugin and included your code in this plugin. ( Also Include calls for your dependant JS files from the plugin)

I have included a link below to the Joomla 2 to 4 plugin documentation.

Create a Joomla plugin

Thank you all.
I finally managed to implement the Draco decoder into my script, taking into account your advice.
And the module works perfectly (with compressed or uncompressed .glb files).
Thank you all.