ThreeJS – Decals are not projected on Surface (glb)

It’s the first time I’m working with ThreeJS on a “more complex” model. After Exporting the OBJ from blender into glb format and import it into my project it works fine – positioning is good and I can add Decals. But when moving or rotating the model the decal position stays but the projection is not “on surface” of the model.

Current state:

const helmet = new Helmet(
    logoTexture,
    new THREE.Color(0x0C072D),
    new THREE.Vector3(0, 10, 0)
);
scene.add(await helmet.generate())

import * as THREE from 'three';
import {GLTFLoader} from "three/addons/loaders/GLTFLoader";

const loader = new GLTFLoader();
import {rotationHelper} from "./utils.js";
import {DecalGeometry} from "three/addons/geometries/DecalGeometry";

export class Helmet {

    logoTexture;
    position;
    rotation;
    color;
    texture;
    isLogoFacingRight;
    shouldMirrorLogo;

    constructor(logoTexture = "", color = new THREE.Color(0xffffff), position = new THREE.Vector3(0, 0, 0), rotation = new THREE.Euler(0, 0, 0), isLogoFacingRight = false, shouldMirrorLogo = false) {
        this.logoTexture = logoTexture;
        this.position = position;
        this.rotation = rotation;
        this.color = color;
        this.texture = null;
        this.isLogoFacingRight = true;
        this.shouldMirrorLogo = false;
    }

    /**
     *
     * @param pos       object          {x,y,z} coordinates unit based for placement in the scene
     * @param rot       object          {x:radiant, y:radiant, z:radiant}
     * @param color     THREE.color     like: THREE.Color(0xffffff)
     * @param texture   THREE.texture   generated via TextureLoader
     * @param isLogoFacingRight bool    True if the Logo image shows to the right
     * @param shouldMirrorLogo  bool    True if the Logo should be mirrored on the opposite side of the helmet
     */
    generate() {
        return new Promise(async (resolve, reject) => {

            // Texturen vorladen
            const metalStrapsTexture = await this.#safeLoadTexture('../models/helmet/helmetFootball_metalStraps_baseColor.png');
            const strapTexture = await this.#safeLoadTexture('../models/helmet/helmetFootball_strap_baseColor.png');

            loader.load(
                './models/helmet/helmetFootball.glb',  // Passen Sie den Pfad entsprechend an
                (gltf) => {
                    const model = gltf.scene.children[0];

                    if (this.logoTexture) {
                        this.#createDecals(model);
                    }

                    model.traverse((node) => {
                        ...
                    });


                    // Optional: Position anpassen
                    model.position.set(
                      this.position.x, 
                      this.position.y, 
                      this.position.z
                    );

                    // Rotation anpassen (falls nötig)
                    model.rotation.set(
                        this.rotation.x,
                        this.rotation.y,
                        this.rotation.z
                    );

                    model.updateMatrix();
                    model.updateMatrixWorld(true); // Erzwingt vollständige Matrixaktualisierung nach rotation


                    resolve(model);
                },
                // Ladefortschritt
                (progress) => {
                    console.log('Lade: ', (progress.loaded / progress.total * 100) + '%');
                },
                // Fehlerbehandlung
                (error) => {
                    console.error('Fehler beim Laden des Modells:', error);
                    reject(error);
                }
            );
        });
    }

    async #createDecals(model) {

        const texture = await this.#safeLoadTexture(this.logoTexture);
        if (!texture) {
            console.log("No Logo Texture found")
            return;
        }

        const targetNode = model.getObjectByName('helmetFootball_4');

        if (!targetNode) {
            console.log("No target for decal found")
        }

        const boundingBox = targetNode.geometry.boundingBox;

        const boundingBoxHelper = new THREE.Box3Helper(boundingBox, 0xff0000); // Rot als Farbe
        targetNode.add(boundingBoxHelper);

        // Decal-Parameter
        const decalSize = new THREE.Vector3(8, 8, 8); // Anpassen je nach Logogröße
        const decalOrientation = new THREE.Euler(0, rotationHelper(90), 0);
        const yPosition = (targetNode.geometry.boundingBox.min.y + targetNode.geometry.boundingBox.max.y) / 2 + 5;


        // Decal-Positionen (links und rechts)
        const decalPositions = [
            new THREE.Vector3(targetNode.geometry.boundingBox.max.x, yPosition, 0),
            new THREE.Vector3(targetNode.geometry.boundingBox.min.x, yPosition, 0)
        ];


        decalPositions.forEach((position, index) => {

            // Helfer zur Viasualisierung der Position
            const helperBox = new THREE.Mesh(
                new THREE.BoxGeometry(8, 8, 8), // Größe der Hilfsbox (x, y, z)
                new THREE.MeshBasicMaterial({
                    color: 0x00ff00, // Grüne Farbe
                    wireframe: true  // Nur Drahtgitter-Ansicht
                })
            );
            helperBox.position.copy(position);
            targetNode.add(helperBox);

            // Decal-Geometrie erstellen
            const decalGeometry = new DecalGeometry(
                targetNode,
                position,
                decalOrientation,
                decalSize
            );

            // Decal-Material
            const decalMaterial = new THREE.MeshBasicMaterial({
                map: texture,
                transparent: true,
                depthTest: true,
                opacity: 1
            });

            // Decal-Mesh erstellen
            const decalMesh = new THREE.Mesh(decalGeometry, decalMaterial);

            // Zum Ziel-Mesh hinzufügen
            targetNode.add(decalMesh);
        });
    }

    #safeLoadTexture(path) {
        return new Promise((resolve, reject) => {
            const textureLoader = new THREE.TextureLoader();
            textureLoader.load(
                path,
                (texture) => resolve(texture),
                undefined, // onProgress
                (error) => {
                    console.warn(`Texture not found: ${path}`, error);
                    // Fallback-Textur oder null zurückgeben
                    resolve(null);
                }
            );
        });
    };
}

Logo images (decals) positioned correctly but not projected onto mesh

As far as i understand it creates the decal projection based on another / the old position and then moves it to the right position? If that is the case how could I solve that?

See the correct placement and projection if the model position is set to 0,0,0:
correct placed if model is set to 0,0,0

Sidenote: the meshes are unstyled because with the way I export it now somehow the mesh titles where not implemented.

thanks for your hints.