itowns
Version:
A JS/WebGL framework for 3D geospatial data visualization
115 lines (111 loc) • 3.67 kB
JavaScript
/**
* Find threejs textures from a material
* @param {Material} material the threejs material holding textures
* @returns {Array} an array of textures in the material
*/
function findTextures(material) {
const textures = [];
if (material.alphaMap) {
textures.push(material.map);
}
if (material.aoMap) {
textures.push(material.map);
}
if (material.bumpMap) {
textures.push(material.bumpMap);
}
if (material.displacementMap) {
textures.push(material.bumpMap);
}
if (material.emissiveMap) {
textures.push(material.emissiveMap);
}
if (material.envMap) {
textures.push(material.envMap);
}
if (material.lightMap) {
textures.push(material.envMap);
}
if (material.map) {
textures.push(material.map);
}
if (material.metalnessMap) {
textures.push(material.map);
}
if (material.normalMap) {
textures.push(material.map);
}
if (material.roughnessMap) {
textures.push(material.map);
}
if (material.specularMap) {
textures.push(material.specularMap);
}
return textures;
}
/**
* Removes a material and its textures, memory will be freed.
* IMPORTANT NOTE: the material and the texture must not be referenced by other threejs objects, otherwise the memory
* won't be freed.
* @param {Material} material the material to remove
*/
export default function disposeThreeMaterial(material) {
const textures = findTextures(material);
// Remove material
if (Array.isArray(material)) {
for (const m of material) {
m.dispose();
}
} else {
material.dispose();
}
// Remove textures
for (let i = 0; i < textures.length; i++) {
textures[i].dispose();
}
}
/**
* Merge groups of an object3D when it can to reduce number of them + remove unused materials
* Reduce draw call https://threejs.org/docs/index.html?q=geometry#api/en/core/BufferGeometry.groups
*
* @param {THREE.Object3D} object3D - object to get optimize
*/
export function optimizeGeometryGroups(object3D) {
if (!object3D.geometry) {
return;
}
object3D.geometry.groups.sort((a, b) => (a.start - b.start) * -1); // [5,10,7] => [10,7,5]
const lastIndex = object3D.geometry.groups.length - 1;
let currentMaterialIndex = object3D.geometry.groups[lastIndex].materialIndex; // initialized with the lastest group
const usedIndexMaterials = [currentMaterialIndex]; // compute materials actually used by group
// for loop descendant to be able to splice in loop without modifying group index
for (let index = lastIndex - 1; index >= 0; index--) {
// begin at lastIndex - 1 because intialized with lastIndex
const group = object3D.geometry.groups[index];
if (group.materialIndex !== currentMaterialIndex) {
// this is another group (!= materialIndex) take its material as ref to continue
currentMaterialIndex = group.materialIndex;
usedIndexMaterials.push(currentMaterialIndex);
continue;
} else {
// indeed same group merge with previous group
const previousGroup = object3D.geometry.groups[index + 1];
previousGroup.count += group.count; // previous group wrap the current one
object3D.geometry.groups.splice(index, 1); // remove group
}
}
// clean unused material
for (let index = object3D.material.length - 1; index >= 0; index--) {
if (!usedIndexMaterials.includes(index)) {
// update all materialIndex in groups
object3D.geometry.groups.forEach(group => {
if (group.materialIndex > index) {
// only materialIndex > at index are modified
group.materialIndex -= 1;
}
});
// remove
object3D.material.splice(index, 1);
}
}
}