@poppod/three-mesh-merger
v0.1.2
Published
TypeScript library for merging multiple 3D GLB files with texture atlas support
Maintainers
Readme
@poppod/three-mesh-merger
A powerful TypeScript library for merging multiple 3D GLB files into a single optimized mesh with flexible texture atlas support.
Features
- 🎯 Merge Multiple GLB Files - Combine any number of 3D models into a single mesh
- 🔄 Independent Transforms - Position, rotate, and scale each model before merging
- 🖼️ Flexible Texture Atlas - Choose which texture maps to combine:
- Albedo/Color maps
- Normal maps
- Roughness maps
- Metalness maps
- Emissive maps
- AO maps
- ⚡ Optimized Output - Single mesh with single material for maximum performance
- 🎨 Material Customization - Override material properties or use averaged values
- 📦 Client-Side Only - Lightweight, browser-native implementation
- 🌳 Tree-Shakeable - Optimized for modern bundlers
- 📘 TypeScript First - Full type safety and IntelliSense support
Installation
pnpm add @poppod/three-mesh-merger three
# or
npm install @poppod/three-mesh-merger three
# or
yarn add @poppod/three-mesh-merger threeQuick Start
import { MeshMerger } from "@poppod/three-mesh-merger";
// Create merger instance
const merger = new MeshMerger();
// Add models with transforms
const cube = await merger.addModel("/models/cube.glb", {
position: [0, 0, 0],
});
const sphere = await merger.addModel("/models/sphere.glb", {
position: [2, 0, 0],
scale: [0.5, 0.5, 0.5],
});
// Merge with options
await merger.merge({
atlasSize: 2048,
textureQuality: 0.9,
atlasMode: {
albedo: true,
normal: true,
roughness: true,
},
});
// Export as GLB
const blob = await merger.export();
const url = URL.createObjectURL(blob);
// Download or use the merged GLBAPI Reference
MeshMerger
Main class for merging 3D models.
Methods
addModel(source: string | Blob, transform?: Transform): Promise<string>
Add a model from URL or Blob.
const id = await merger.addModel("/model.glb", {
position: [0, 1, 0],
rotation: [0, Math.PI / 4, 0],
scale: [1, 1, 1],
});Parameters:
source: URL string or Blob/File objecttransform(optional): Initial transform
Returns: Model ID
updateTransform(id: string, transform: Partial<Transform>): void
Update transform for a specific model.
merger.updateTransform(id, {
position: [1, 0, 0],
});removeModel(id: string): void
Remove a model from the merger.
merger.removeModel(id);getModels(): ModelInstance[]
Get all loaded models.
const models = merger.getModels();getModel(id: string): ModelInstance | undefined
Get a specific model by ID.
const model = merger.getModel(id);merge(options?: MergeOptions): Promise<void>
Merge all models with specified options.
await merger.merge({
atlasSize: 2048,
textureQuality: 0.9,
atlasMode: {
albedo: true,
normal: true,
roughness: true,
metalness: true,
},
materialOverrides: {
roughness: 0.5,
metalness: 0.8,
},
});export(): Promise<Blob>
Export merged result as GLB Blob.
const blob = await merger.export();
const url = URL.createObjectURL(blob);
// Download
const link = document.createElement("a");
link.href = url;
link.download = "merged.glb";
link.click();getMergedScene(): THREE.Scene | undefined
Get the merged scene for preview.
getMergedMesh(): THREE.Mesh | undefined
Get the merged mesh for preview.
setProgressCallback(callback: ProgressCallback): void
Set progress callback for merge operations.
merger.setProgressCallback((stage, progress) => {
console.log(`${stage}: ${progress * 100}%`);
});clear(): void
Clear all models and merged result.
merger.clear();Types
Transform
interface Transform {
position?: [number, number, number];
rotation?: [number, number, number]; // Euler angles in radians
scale?: [number, number, number];
}MergeOptions
interface MergeOptions {
atlasSize?: number; // Default: 2048
textureQuality?: number; // 0-1, Default: 0.9
generateMipmaps?: boolean; // Default: true
atlasMode?: AtlasMode;
materialOverrides?: MaterialOverrides;
}AtlasMode
interface AtlasMode {
albedo?: boolean; // Default: true
normal?: boolean; // Default: false
roughness?: boolean; // Default: false
metalness?: boolean; // Default: false
emissive?: boolean; // Default: false
aoMap?: boolean; // Default: false
}MaterialOverrides
interface MaterialOverrides {
roughness?: number;
metalness?: number;
color?: number | string; // THREE.Color compatible
emissive?: number | string;
emissiveIntensity?: number;
}ProgressCallback
type ProgressCallback = (stage: string, progress: number) => void;Usage with Frameworks
React
import { MeshMerger } from "@poppod/three-mesh-merger";
import { useEffect, useRef } from "react";
function MyComponent() {
const mergerRef = useRef(new MeshMerger());
const handleMerge = async () => {
await mergerRef.current.addModel("/model1.glb");
await mergerRef.current.addModel("/model2.glb");
await mergerRef.current.merge();
const blob = await mergerRef.current.export();
// Handle blob
};
return <button onClick={handleMerge}>Merge</button>;
}Next.js
"use client";
import { MeshMerger } from "@poppod/three-mesh-merger";
import { useState } from "react";
export default function MergePage() {
const [merger] = useState(() => new MeshMerger());
// Your implementation
}Vue 3
<script setup>
import { MeshMerger } from "@poppod/three-mesh-merger";
import { ref } from "vue";
const merger = ref(new MeshMerger());
const handleMerge = async () => {
await merger.value.addModel("/model.glb");
await merger.value.merge();
const blob = await merger.value.export();
// Handle blob
};
</script>How It Works
- Load Models: GLB files are loaded using Three.js GLTFLoader
- Apply Transforms: Each model can be positioned, rotated, and scaled independently
- Geometry Merging: All geometries are merged into a single BufferGeometry
- Texture Atlas: Textures are packed into atlases using potpack algorithm
- UV Remapping: UV coordinates are updated to match atlas layout
- Material Creation: Single material is created with all atlas textures
- Export: Final merged model is exported as GLB using Three.js GLTFExporter
Performance Considerations
- Atlas Size: Larger atlas = better quality but more memory
- Texture Quality: Lower quality = smaller file size
- Map Selection: Only enable needed maps to save memory
- Model Count: More models = longer merge time
Browser Support
- Modern browsers with WebGL support
- ES2020+ JavaScript features
- Canvas API for texture processing
Peer Dependencies
three>= 0.150.0
License
MIT © poppod
