@google/model-viewer
Version:
Easily display interactive 3D models on the web and in AR!
113 lines • 4.45 kB
JavaScript
/*
* Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Skeleton } from 'three';
import { cubeUVChunk } from './shader-chunk/cube_uv_reflection_fragment.glsl.js';
import { envmapChunk } from './shader-chunk/envmap_physical_pars_fragment.glsl.js';
import { lightsChunk } from './shader-chunk/lights_fragment_maps.glsl.js';
import { normalmapChunk } from './shader-chunk/normalmap_pars_fragment.glsl.js';
/**
* This is a patch to Three.js' handling of PMREM environments. This patch
* has to be applied after cloning because Three.js does not seem to clone
* the onBeforeCompile method.
*/
const updateShader = (shader) => {
shader.fragmentShader =
shader.fragmentShader
.replace('#include <cube_uv_reflection_fragment>', cubeUVChunk)
.replace('#include <envmap_physical_pars_fragment>', envmapChunk)
.replace('#include <lights_fragment_maps>', lightsChunk)
.replace('#include <normalmap_pars_fragment>', normalmapChunk);
};
/**
* Creates a clone of the given material, and applies a patch to the
* shader program.
*/
const cloneAndPatchMaterial = (material) => {
const clone = material.clone();
clone.onBeforeCompile = updateShader;
return clone;
};
/**
* Fully clones a parsed GLTF, including correct cloning of any SkinnedMesh
* objects.
*
* NOTE(cdata): This is necessary due to limitations of the Three.js clone
* routine on scenes. Without it, models with skeletal animations will not be
* cloned properly.
*
* @see https://github.com/mrdoob/three.js/issues/5878
*/
export const cloneGltf = (gltf) => {
const hasScene = gltf.scene != null;
const clone = Object.assign({}, gltf, { scene: hasScene ? gltf.scene.clone(true) : null });
const skinnedMeshes = {};
let hasSkinnedMeshes = false;
if (hasScene) {
gltf.scene.traverse((node) => {
if (node.isSkinnedMesh) {
hasSkinnedMeshes = true;
skinnedMeshes[node.name] = node;
}
});
}
const cloneBones = {};
const cloneSkinnedMeshes = {};
if (hasScene) {
clone.scene.traverse((node) => {
// Set a high renderOrder while we're here to ensure the model
// always renders on top of the skysphere
node.renderOrder = 1000;
// Materials aren't cloned when cloning meshes; geometry
// and materials are copied by reference. This is necessary
// for the same model to be used twice with different
// environment maps.
if (Array.isArray(node.material)) {
node.material = node.material.map(cloneAndPatchMaterial);
}
else if (node.material != null) {
node.material = cloneAndPatchMaterial(node.material);
}
if (hasSkinnedMeshes) {
if (node.isBone) {
cloneBones[node.name] = node;
}
if (node.isSkinnedMesh) {
cloneSkinnedMeshes[node.name] = node;
}
}
});
}
for (let name in skinnedMeshes) {
const skinnedMesh = skinnedMeshes[name];
const skeleton = skinnedMesh.skeleton;
const cloneSkinnedMesh = cloneSkinnedMeshes[name];
const orderedCloneBones = [];
for (let i = 0; i < skeleton.bones.length; ++i) {
const cloneBone = cloneBones[skeleton.bones[i].name];
orderedCloneBones.push(cloneBone);
}
cloneSkinnedMesh.bind(new Skeleton(orderedCloneBones, skeleton.boneInverses), cloneSkinnedMesh.matrixWorld);
}
return clone;
};
/**
* Moves Three.js objects from one parent to another
*/
export const moveChildren = (from, to) => {
while (from.children.length) {
to.add(from.children.shift());
}
};
//# sourceMappingURL=ModelUtils.js.map