why don’t you send data like buffer geometry, so this is perfect way. and in client you can use BufferGeometry to make mesh lighter and easy to load
import { DRACOLoader, GLTFLoader } from "three/examples/jsm/Addons.js";
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader.js";
self.onmessage = async (event) => {
const { url, renderer } = event.data;
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("https://unpkg.com/three@0.158.0/examples/jsm/libs/draco/");
dracoLoader.setDecoderConfig({ type: "js" });
dracoLoader.setWorkerLimit(4);
dracoLoader.preload();
const ktx2Loader = new KTX2Loader()
.setTranscoderPath('https://unpkg.com/three/examples/jsm/libs/basis/')
.detectSupport(renderer);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.setKTX2Loader(ktx2Loader);
let cVertex = 0;
let cIndex = 0;
loader.load(url, async (gltf) => {
let meshes = [];
const images = gltf.parser.json.images ? gltf.parser.json.images.map(img => img.uri || img.name) : [];
gltf.scene.traverse((child) => {
if (child.isMesh) {
let geometry = child.geometry;
if (!geometry.boundingBox) geometry.computeBoundingBox();
if (!geometry.boundingSphere) geometry.computeBoundingSphere();
const positions = geometry?.attributes.position ? new Float32Array(geometry.attributes.position.array) : null;
const normals = geometry?.attributes.normal ? new Float32Array(geometry.attributes.normal.array) : null;
const uvs = geometry?.attributes.uv ? new Float32Array(geometry.attributes.uv.array) : null;
const indices = geometry?.index ? new Uint32Array(geometry.index.array) : null;
cVertex += geometry.attributes.position.count;
cIndex += geometry.index.count
meshes.push({
name: child.name,
scale: child.scale.toArray(),
attributes: {
positions: positions?.buffer,
normals: normals?.buffer !== null ? normals?.buffer : null,
uvs: uvs?.buffer,
indices: indices?.buffer
},
boundingBox: geometry.boundingBox,
boundingSphere: geometry.boundingSphere,
material: child.material ? extractMaterialInfo(child.material, url) : null
});
}
});
self.postMessage({
type: 'GLTFMeshes', meshes, images
});
});
};
function extractMaterialInfo(material, url) {
if (!material) return null;
const basePath = url.split('/').slice(0, -1).join('/') + '/';
return {
name: material.name || "Unnamed",
color: material.color ? material.color.getHexString() : null,
metalness: material.metalness !== undefined ? material.metalness : null,
roughness: material.roughness !== undefined ? material.roughness : null,
ior: material.ior !== undefined ? material.ior : null,
clearcoat: material.clearcoat !== undefined ? material.clearcoat : null,
clearcoatRoughness: material.clearcoatRoughness !== undefined ? material.clearcoatRoughness : null,
sheen: material.sheen !== undefined ? material.sheen : null,
sheenColor: material.sheenColor ? material.sheenColor.getHexString() : null,
sheenRoughness: material.sheenRoughness !== undefined ? material.sheenRoughness : null,
transmission: material.transmission !== undefined ? material.transmission : null,
thickness: material.thickness !== undefined ? material.thickness : null,
attenuationDistance: material.attenuationDistance !== undefined ? material.attenuationDistance : null,
attenuationColor: material.attenuationColor ? material.attenuationColor.getHexString() : null,
reflectivity: material.reflectivity !== undefined ? material.reflectivity : null,
specularIntensity: material.specularIntensity !== undefined ? material.specularIntensity : null,
specularColor: material.specularColor ? material.specularColor.getHexString() : null,
emissive: material.emissive ? material.emissive.getHexString() : null,
emissiveIntensity: material.emissiveIntensity !== undefined ? material.emissiveIntensity : null,
opacity: material.opacity !== undefined ? material.opacity : null,
transparent: material.transparent !== undefined ? material.transparent : null,
envMapIntensity: material.envMapIntensity !== undefined ? material.envMapIntensity : null,
// Extract textures
map: extractTextureInfo(material.map, basePath),
normalMap: extractTextureInfo(material.normalMap, basePath),
roughnessMap: extractTextureInfo(material.roughnessMap, basePath),
metalnessMap: extractTextureInfo(material.metalnessMap, basePath),
aoMap: extractTextureInfo(material.aoMap, basePath),
emissiveMap: extractTextureInfo(material.emissiveMap, basePath),
clearcoatMap: extractTextureInfo(material.clearcoatMap, basePath),
alphaMap: extractTextureInfo(material.alphaMap, basePath)
};
}
function extractTextureInfo(texture, basePath) {
if (!texture || !texture.image) return null;
return {
name: texture.name,
url: `${basePath}textures/${texture.name}.ktx2` || null,
width: texture.image.width || null,
height: texture.image.height || null
};
}