@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
899 lines • 55.2 kB
JavaScript
import { Logger } from "../../Misc/logger.js";
import { Camera } from "../../Cameras/camera.js";
import { Vector3 } from "../../Maths/math.vector.js";
import { Color3, Color4 } from "../../Maths/math.color.js";
import { Mesh } from "../../Meshes/mesh.js";
import { Geometry } from "../../Meshes/geometry.js";
import { TransformNode } from "../../Meshes/transformNode.js";
import { Material } from "../../Materials/material.js";
import { MultiMaterial } from "../../Materials/multiMaterial.js";
import { CubeTexture } from "../../Materials/Textures/cubeTexture.js";
import { HDRCubeTexture } from "../../Materials/Textures/hdrCubeTexture.js";
import { AnimationGroup } from "../../Animations/animationGroup.js";
import { Light } from "../../Lights/light.js";
import { SceneComponentConstants } from "../../sceneComponent.js";
import { SceneLoader } from "../../Loading/sceneLoader.js";
import { AssetContainer } from "../../assetContainer.js";
import { ActionManager } from "../../Actions/actionManager.js";
import { Skeleton } from "../../Bones/skeleton.js";
import { MorphTargetManager } from "../../Morph/morphTargetManager.js";
import { CannonJSPlugin } from "../../Physics/v1/Plugins/cannonJSPlugin.js";
import { OimoJSPlugin } from "../../Physics/v1/Plugins/oimoJSPlugin.js";
import { AmmoJSPlugin } from "../../Physics/v1/Plugins/ammoJSPlugin.js";
import { ReflectionProbe } from "../../Probes/reflectionProbe.js";
import { GetClass } from "../../Misc/typeStore.js";
import { Tools } from "../../Misc/tools.js";
import { PostProcess } from "../../PostProcesses/postProcess.js";
import { SpriteManager } from "../../Sprites/spriteManager.js";
import { GetIndividualParser, Parse } from "./babylonFileParser.function.js";
/** @internal */
// eslint-disable-next-line @typescript-eslint/naming-convention, no-var
export var _BabylonLoaderRegistered = true;
/**
* Helps setting up some configuration for the babylon file loader.
*/
export class BabylonFileLoaderConfiguration {
}
/**
* The loader does not allow injecting custom physics engine into the plugins.
* Unfortunately in ES6, we need to manually inject them into the plugin.
* So you could set this variable to your engine import to make it work.
*/
BabylonFileLoaderConfiguration.LoaderInjectedPhysicsEngine = undefined;
let TempIndexContainer = {};
let TempMaterialIndexContainer = {};
let TempMorphTargetManagerIndexContainer = {};
const ParseMaterialByPredicate = (predicate, parsedData, scene, rootUrl) => {
if (!parsedData.materials) {
return null;
}
for (let index = 0, cache = parsedData.materials.length; index < cache; index++) {
const parsedMaterial = parsedData.materials[index];
if (predicate(parsedMaterial)) {
return { parsedMaterial, material: Material.Parse(parsedMaterial, scene, rootUrl) };
}
}
return null;
};
const IsDescendantOf = (mesh, names, hierarchyIds) => {
for (const i in names) {
if (mesh.name === names[i]) {
hierarchyIds.push(mesh.id);
return true;
}
}
if (mesh.parentId !== undefined && hierarchyIds.indexOf(mesh.parentId) !== -1) {
hierarchyIds.push(mesh.id);
return true;
}
return false;
};
// eslint-disable-next-line @typescript-eslint/naming-convention
const logOperation = (operation, producer) => {
return (operation +
" of " +
(producer ? producer.file + " from " + producer.name + " version: " + producer.version + ", exporter version: " + producer.exporter_version : "unknown"));
};
const LoadDetailLevels = (scene, mesh) => {
const mastermesh = mesh;
// Every value specified in the ids array of the lod data points to another mesh which should be used as the lower LOD level.
// The distances (or coverages) array values specified are used along with the lod mesh ids as a hint to determine the switching threshold for the various LODs.
if (mesh._waitingData.lods) {
if (mesh._waitingData.lods.ids && mesh._waitingData.lods.ids.length > 0) {
const lodmeshes = mesh._waitingData.lods.ids;
const wasenabled = mastermesh.isEnabled(false);
if (mesh._waitingData.lods.distances) {
const distances = mesh._waitingData.lods.distances;
if (distances.length >= lodmeshes.length) {
const culling = distances.length > lodmeshes.length ? distances[distances.length - 1] : 0;
mastermesh.setEnabled(false);
for (let index = 0; index < lodmeshes.length; index++) {
const lodid = lodmeshes[index];
const lodmesh = scene.getMeshById(lodid);
if (lodmesh != null) {
mastermesh.addLODLevel(distances[index], lodmesh);
}
}
if (culling > 0) {
mastermesh.addLODLevel(culling, null);
}
if (wasenabled === true) {
mastermesh.setEnabled(true);
}
}
else {
Tools.Warn("Invalid level of detail distances for " + mesh.name);
}
}
}
mesh._waitingData.lods = null;
}
};
const FindParent = (parentId, parentInstanceIndex, scene) => {
if (typeof parentId !== "number") {
const parentEntry = scene.getLastEntryById(parentId);
if (parentEntry && parentInstanceIndex !== undefined && parentInstanceIndex !== null) {
const instance = parentEntry.instances[parseInt(parentInstanceIndex)];
return instance;
}
return parentEntry;
}
const parent = TempIndexContainer[parentId];
if (parent && parentInstanceIndex !== undefined && parentInstanceIndex !== null) {
const instance = parent.instances[parseInt(parentInstanceIndex)];
return instance;
}
return parent;
};
const FindMaterial = (materialId, scene) => {
if (typeof materialId !== "number") {
return scene.getLastMaterialById(materialId, true);
}
return TempMaterialIndexContainer[materialId];
};
const LoadAssetContainer = (scene, data, rootUrl, onError, addToScene = false) => {
const container = new AssetContainer(scene);
// Entire method running in try block, so ALWAYS logs as far as it got, only actually writes details
// when SceneLoader.debugLogging = true (default), or exception encountered.
// Everything stored in var log instead of writing separate lines to support only writing in exception,
// and avoid problems with multiple concurrent .babylon loads.
let log = "importScene has failed JSON parse";
try {
// eslint-disable-next-line no-var
var parsedData = JSON.parse(data);
log = "";
const fullDetails = SceneLoader.loggingLevel === SceneLoader.DETAILED_LOGGING;
let index;
let cache;
// Environment texture
if (parsedData.environmentTexture !== undefined && parsedData.environmentTexture !== null) {
// PBR needed for both HDR texture (gamma space) & a sky box
const isPBR = parsedData.isPBR !== undefined ? parsedData.isPBR : true;
if (parsedData.environmentTextureType && parsedData.environmentTextureType === "BABYLON.HDRCubeTexture") {
const hdrSize = parsedData.environmentTextureSize ? parsedData.environmentTextureSize : 128;
const hdrTexture = new HDRCubeTexture((parsedData.environmentTexture.match(/https?:\/\//g) ? "" : rootUrl) + parsedData.environmentTexture, scene, hdrSize, true, !isPBR, undefined, parsedData.environmentTexturePrefilterOnLoad);
if (parsedData.environmentTextureRotationY) {
hdrTexture.rotationY = parsedData.environmentTextureRotationY;
}
scene.environmentTexture = hdrTexture;
}
else {
if (typeof parsedData.environmentTexture === "object") {
const environmentTexture = CubeTexture.Parse(parsedData.environmentTexture, scene, rootUrl);
scene.environmentTexture = environmentTexture;
}
else if (parsedData.environmentTexture.endsWith(".env")) {
const compressedTexture = new CubeTexture((parsedData.environmentTexture.match(/https?:\/\//g) ? "" : rootUrl) + parsedData.environmentTexture, scene, parsedData.environmentTextureForcedExtension);
if (parsedData.environmentTextureRotationY) {
compressedTexture.rotationY = parsedData.environmentTextureRotationY;
}
scene.environmentTexture = compressedTexture;
}
else {
const cubeTexture = CubeTexture.CreateFromPrefilteredData((parsedData.environmentTexture.match(/https?:\/\//g) ? "" : rootUrl) + parsedData.environmentTexture, scene, parsedData.environmentTextureForcedExtension);
if (parsedData.environmentTextureRotationY) {
cubeTexture.rotationY = parsedData.environmentTextureRotationY;
}
scene.environmentTexture = cubeTexture;
}
}
if (parsedData.createDefaultSkybox === true) {
const skyboxScale = scene.activeCamera !== undefined && scene.activeCamera !== null ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
const skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
scene.createDefaultSkybox(scene.environmentTexture, isPBR, skyboxScale, skyboxBlurLevel);
}
container.environmentTexture = scene.environmentTexture;
}
// Environment Intensity
if (parsedData.environmentIntensity !== undefined && parsedData.environmentIntensity !== null) {
scene.environmentIntensity = parsedData.environmentIntensity;
}
// IBL Intensity
if (parsedData.iblIntensity !== undefined && parsedData.iblIntensity !== null) {
scene.iblIntensity = parsedData.iblIntensity;
}
// Lights
if (parsedData.lights !== undefined && parsedData.lights !== null) {
for (index = 0, cache = parsedData.lights.length; index < cache; index++) {
const parsedLight = parsedData.lights[index];
const light = Light.Parse(parsedLight, scene);
if (light) {
TempIndexContainer[parsedLight.uniqueId] = light;
container.lights.push(light);
light._parentContainer = container;
log += index === 0 ? "\n\tLights:" : "";
log += "\n\t\t" + light.toString(fullDetails);
}
}
}
// Reflection probes
if (parsedData.reflectionProbes !== undefined && parsedData.reflectionProbes !== null) {
for (index = 0, cache = parsedData.reflectionProbes.length; index < cache; index++) {
const parsedReflectionProbe = parsedData.reflectionProbes[index];
const reflectionProbe = ReflectionProbe.Parse(parsedReflectionProbe, scene, rootUrl);
if (reflectionProbe) {
container.reflectionProbes.push(reflectionProbe);
reflectionProbe._parentContainer = container;
log += index === 0 ? "\n\tReflection Probes:" : "";
log += "\n\t\t" + reflectionProbe.toString(fullDetails);
}
}
}
// Animations
if (parsedData.animations !== undefined && parsedData.animations !== null) {
for (index = 0, cache = parsedData.animations.length; index < cache; index++) {
const parsedAnimation = parsedData.animations[index];
const internalClass = GetClass("BABYLON.Animation");
if (internalClass) {
const animation = internalClass.Parse(parsedAnimation);
scene.animations.push(animation);
container.animations.push(animation);
log += index === 0 ? "\n\tAnimations:" : "";
log += "\n\t\t" + animation.toString(fullDetails);
}
}
}
// Materials
if (parsedData.materials !== undefined && parsedData.materials !== null) {
for (index = 0, cache = parsedData.materials.length; index < cache; index++) {
const parsedMaterial = parsedData.materials[index];
const mat = Material.Parse(parsedMaterial, scene, rootUrl);
if (mat) {
TempMaterialIndexContainer[parsedMaterial.uniqueId || parsedMaterial.id] = mat;
container.materials.push(mat);
mat._parentContainer = container;
log += index === 0 ? "\n\tMaterials:" : "";
log += "\n\t\t" + mat.toString(fullDetails);
// Textures
const textures = mat.getActiveTextures();
for (const t of textures) {
if (container.textures.indexOf(t) == -1) {
container.textures.push(t);
t._parentContainer = container;
}
}
}
}
}
if (parsedData.multiMaterials !== undefined && parsedData.multiMaterials !== null) {
for (index = 0, cache = parsedData.multiMaterials.length; index < cache; index++) {
const parsedMultiMaterial = parsedData.multiMaterials[index];
const mmat = MultiMaterial.ParseMultiMaterial(parsedMultiMaterial, scene);
TempMaterialIndexContainer[parsedMultiMaterial.uniqueId || parsedMultiMaterial.id] = mmat;
container.multiMaterials.push(mmat);
mmat._parentContainer = container;
log += index === 0 ? "\n\tMultiMaterials:" : "";
log += "\n\t\t" + mmat.toString(fullDetails);
// Textures
const textures = mmat.getActiveTextures();
for (const t of textures) {
if (container.textures.indexOf(t) == -1) {
container.textures.push(t);
t._parentContainer = container;
}
}
}
}
// Morph targets
if (parsedData.morphTargetManagers !== undefined && parsedData.morphTargetManagers !== null) {
for (const parsedManager of parsedData.morphTargetManagers) {
const manager = MorphTargetManager.Parse(parsedManager, scene);
TempMorphTargetManagerIndexContainer[parsedManager.id] = manager;
container.morphTargetManagers.push(manager);
manager._parentContainer = container;
}
}
// Skeletons
if (parsedData.skeletons !== undefined && parsedData.skeletons !== null) {
for (index = 0, cache = parsedData.skeletons.length; index < cache; index++) {
const parsedSkeleton = parsedData.skeletons[index];
const skeleton = Skeleton.Parse(parsedSkeleton, scene);
container.skeletons.push(skeleton);
skeleton._parentContainer = container;
log += index === 0 ? "\n\tSkeletons:" : "";
log += "\n\t\t" + skeleton.toString(fullDetails);
}
}
// Geometries
const geometries = parsedData.geometries;
if (geometries !== undefined && geometries !== null) {
const addedGeometry = new Array();
// VertexData
const vertexData = geometries.vertexData;
if (vertexData !== undefined && vertexData !== null) {
for (index = 0, cache = vertexData.length; index < cache; index++) {
const parsedVertexData = vertexData[index];
addedGeometry.push(Geometry.Parse(parsedVertexData, scene, rootUrl));
}
}
for (const g of addedGeometry) {
if (g) {
container.geometries.push(g);
g._parentContainer = container;
}
}
}
// Transform nodes
if (parsedData.transformNodes !== undefined && parsedData.transformNodes !== null) {
for (index = 0, cache = parsedData.transformNodes.length; index < cache; index++) {
const parsedTransformNode = parsedData.transformNodes[index];
const node = TransformNode.Parse(parsedTransformNode, scene, rootUrl);
TempIndexContainer[parsedTransformNode.uniqueId] = node;
container.transformNodes.push(node);
node._parentContainer = container;
}
}
// Meshes
if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
const parsedMesh = parsedData.meshes[index];
const mesh = Mesh.Parse(parsedMesh, scene, rootUrl);
TempIndexContainer[parsedMesh.uniqueId] = mesh;
container.meshes.push(mesh);
mesh._parentContainer = container;
if (mesh.hasInstances) {
for (const instance of mesh.instances) {
container.meshes.push(instance);
instance._parentContainer = container;
}
}
log += index === 0 ? "\n\tMeshes:" : "";
log += "\n\t\t" + mesh.toString(fullDetails);
}
}
// Cameras
if (parsedData.cameras !== undefined && parsedData.cameras !== null) {
for (index = 0, cache = parsedData.cameras.length; index < cache; index++) {
const parsedCamera = parsedData.cameras[index];
const camera = Camera.Parse(parsedCamera, scene);
TempIndexContainer[parsedCamera.uniqueId] = camera;
container.cameras.push(camera);
camera._parentContainer = container;
log += index === 0 ? "\n\tCameras:" : "";
log += "\n\t\t" + camera.toString(fullDetails);
}
}
// Postprocesses
if (parsedData.postProcesses !== undefined && parsedData.postProcesses !== null) {
for (index = 0, cache = parsedData.postProcesses.length; index < cache; index++) {
const parsedPostProcess = parsedData.postProcesses[index];
const postProcess = PostProcess.Parse(parsedPostProcess, scene, rootUrl);
if (postProcess) {
container.postProcesses.push(postProcess);
postProcess._parentContainer = container;
log += index === 0 ? "\nPostprocesses:" : "";
log += "\n\t\t" + postProcess.toString();
}
}
}
// Animation Groups
if (parsedData.animationGroups !== undefined && parsedData.animationGroups !== null && parsedData.animationGroups.length) {
// Build the nodeMap only for scenes with animationGroups
const nodeMap = new Map();
// Nodes in scene does not change when parsing animationGroups, so it's safe to build a map.
// This follows the order of scene.getNodeById: mesh, transformNode, light, camera, bone
for (let index = 0; index < scene.meshes.length; index++) {
// This follows the behavior of scene.getXXXById, which picks the first match
if (!nodeMap.has(scene.meshes[index].id)) {
nodeMap.set(scene.meshes[index].id, scene.meshes[index]);
}
}
for (let index = 0; index < scene.transformNodes.length; index++) {
if (!nodeMap.has(scene.transformNodes[index].id)) {
nodeMap.set(scene.transformNodes[index].id, scene.transformNodes[index]);
}
}
for (let index = 0; index < scene.lights.length; index++) {
if (!nodeMap.has(scene.lights[index].id)) {
nodeMap.set(scene.lights[index].id, scene.lights[index]);
}
}
for (let index = 0; index < scene.cameras.length; index++) {
if (!nodeMap.has(scene.cameras[index].id)) {
nodeMap.set(scene.cameras[index].id, scene.cameras[index]);
}
}
for (let skeletonIndex = 0; skeletonIndex < scene.skeletons.length; skeletonIndex++) {
const skeleton = scene.skeletons[skeletonIndex];
for (let boneIndex = 0; boneIndex < skeleton.bones.length; boneIndex++) {
if (!nodeMap.has(skeleton.bones[boneIndex].id)) {
nodeMap.set(skeleton.bones[boneIndex].id, skeleton.bones[boneIndex]);
}
}
}
for (index = 0, cache = parsedData.animationGroups.length; index < cache; index++) {
const parsedAnimationGroup = parsedData.animationGroups[index];
const animationGroup = AnimationGroup.Parse(parsedAnimationGroup, scene, nodeMap);
container.animationGroups.push(animationGroup);
animationGroup._parentContainer = container;
log += index === 0 ? "\n\tAnimationGroups:" : "";
log += "\n\t\t" + animationGroup.toString(fullDetails);
}
}
// Sprites
if (parsedData.spriteManagers) {
for (let index = 0, cache = parsedData.spriteManagers.length; index < cache; index++) {
const parsedSpriteManager = parsedData.spriteManagers[index];
const spriteManager = SpriteManager.Parse(parsedSpriteManager, scene, rootUrl);
log += "\n\t\tSpriteManager " + spriteManager.name;
}
}
// Browsing all the graph to connect the dots
for (index = 0, cache = scene.cameras.length; index < cache; index++) {
const camera = scene.cameras[index];
if (camera._waitingParentId !== null) {
camera.parent = FindParent(camera._waitingParentId, camera._waitingParentInstanceIndex, scene);
camera._waitingParentId = null;
camera._waitingParentInstanceIndex = null;
}
}
for (index = 0, cache = scene.lights.length; index < cache; index++) {
const light = scene.lights[index];
if (light && light._waitingParentId !== null) {
light.parent = FindParent(light._waitingParentId, light._waitingParentInstanceIndex, scene);
light._waitingParentId = null;
light._waitingParentInstanceIndex = null;
}
}
// Connect parents & children and parse actions and lods
for (index = 0, cache = scene.transformNodes.length; index < cache; index++) {
const transformNode = scene.transformNodes[index];
if (transformNode._waitingParentId !== null) {
transformNode.parent = FindParent(transformNode._waitingParentId, transformNode._waitingParentInstanceIndex, scene);
transformNode._waitingParentId = null;
transformNode._waitingParentInstanceIndex = null;
}
}
for (index = 0, cache = scene.meshes.length; index < cache; index++) {
const mesh = scene.meshes[index];
if (mesh._waitingParentId !== null) {
mesh.parent = FindParent(mesh._waitingParentId, mesh._waitingParentInstanceIndex, scene);
mesh._waitingParentId = null;
mesh._waitingParentInstanceIndex = null;
}
if (mesh._waitingData.lods) {
LoadDetailLevels(scene, mesh);
}
}
// link multimats with materials
for (const multimat of scene.multiMaterials) {
for (const subMaterial of multimat._waitingSubMaterialsUniqueIds) {
multimat.subMaterials.push(FindMaterial(subMaterial, scene));
}
multimat._waitingSubMaterialsUniqueIds = [];
}
// link meshes with materials
for (const mesh of scene.meshes) {
if (mesh._waitingMaterialId !== null) {
mesh.material = FindMaterial(mesh._waitingMaterialId, scene);
mesh._waitingMaterialId = null;
}
}
// link meshes with morph target managers
for (const mesh of scene.meshes) {
if (mesh._waitingMorphTargetManagerId !== null) {
mesh.morphTargetManager = TempMorphTargetManagerIndexContainer[mesh._waitingMorphTargetManagerId];
mesh._waitingMorphTargetManagerId = null;
}
}
// link skeleton transform nodes
for (index = 0, cache = scene.skeletons.length; index < cache; index++) {
const skeleton = scene.skeletons[index];
if (skeleton._hasWaitingData) {
if (skeleton.bones != null) {
for (const bone of skeleton.bones) {
if (bone._waitingTransformNodeId) {
const linkTransformNode = scene.getLastEntryById(bone._waitingTransformNodeId);
if (linkTransformNode) {
bone.linkTransformNode(linkTransformNode);
}
bone._waitingTransformNodeId = null;
}
}
}
skeleton._hasWaitingData = null;
}
}
// freeze world matrix application
for (index = 0, cache = scene.meshes.length; index < cache; index++) {
const currentMesh = scene.meshes[index];
if (currentMesh._waitingData.freezeWorldMatrix) {
currentMesh.freezeWorldMatrix();
currentMesh._waitingData.freezeWorldMatrix = null;
}
else {
currentMesh.computeWorldMatrix(true);
}
}
// Lights exclusions / inclusions
for (index = 0, cache = scene.lights.length; index < cache; index++) {
const light = scene.lights[index];
// Excluded check
if (light._excludedMeshesIds.length > 0) {
for (let excludedIndex = 0; excludedIndex < light._excludedMeshesIds.length; excludedIndex++) {
const excludedMesh = scene.getMeshById(light._excludedMeshesIds[excludedIndex]);
if (excludedMesh) {
light.excludedMeshes.push(excludedMesh);
}
}
light._excludedMeshesIds = [];
}
// Included check
if (light._includedOnlyMeshesIds.length > 0) {
for (let includedOnlyIndex = 0; includedOnlyIndex < light._includedOnlyMeshesIds.length; includedOnlyIndex++) {
const includedOnlyMesh = scene.getMeshById(light._includedOnlyMeshesIds[includedOnlyIndex]);
if (includedOnlyMesh) {
light.includedOnlyMeshes.push(includedOnlyMesh);
}
}
light._includedOnlyMeshesIds = [];
}
}
for (const g of scene.geometries) {
g._loadedUniqueId = "";
}
Parse(parsedData, scene, container, rootUrl);
// Actions (scene) Done last as it can access other objects.
for (index = 0, cache = scene.meshes.length; index < cache; index++) {
const mesh = scene.meshes[index];
if (mesh._waitingData.actions) {
ActionManager.Parse(mesh._waitingData.actions, mesh, scene);
mesh._waitingData.actions = null;
}
}
if (parsedData.actions !== undefined && parsedData.actions !== null) {
ActionManager.Parse(parsedData.actions, null, scene);
}
}
catch (err) {
const msg = logOperation("loadAssets", parsedData ? parsedData.producer : "Unknown") + log;
if (onError) {
onError(msg, err);
}
else {
Logger.Log(msg);
throw err;
}
}
finally {
TempIndexContainer = {};
TempMaterialIndexContainer = {};
TempMorphTargetManagerIndexContainer = {};
if (!addToScene) {
container.removeAllFromScene();
}
if (log !== null && SceneLoader.loggingLevel !== SceneLoader.NO_LOGGING) {
Logger.Log(logOperation("loadAssets", parsedData ? parsedData.producer : "Unknown") + (SceneLoader.loggingLevel !== SceneLoader.MINIMAL_LOGGING ? log : ""));
}
}
return container;
};
SceneLoader.RegisterPlugin({
name: "babylon.js",
extensions: ".babylon",
canDirectLoad: (data) => {
if (data.indexOf("babylon") !== -1) {
// We consider that the producer string is filled
return true;
}
return false;
},
importMesh: (meshesNames, scene, data, rootUrl, meshes, particleSystems, skeletons, onError) => {
// Entire method running in try block, so ALWAYS logs as far as it got, only actually writes details
// when SceneLoader.debugLogging = true (default), or exception encountered.
// Everything stored in var log instead of writing separate lines to support only writing in exception,
// and avoid problems with multiple concurrent .babylon loads.
let log = "importMesh has failed JSON parse";
try {
// eslint-disable-next-line no-var
var parsedData = JSON.parse(data);
log = "";
const fullDetails = SceneLoader.loggingLevel === SceneLoader.DETAILED_LOGGING;
if (!meshesNames) {
meshesNames = null;
}
else if (!Array.isArray(meshesNames)) {
meshesNames = [meshesNames];
}
const hierarchyIds = [];
const parsedIdToNodeMap = new Map();
// Transform nodes (the overall idea is to load all of them as this is super fast and then get rid of the ones we don't need)
const loadedTransformNodes = [];
if (parsedData.transformNodes !== undefined && parsedData.transformNodes !== null) {
for (let index = 0, cache = parsedData.transformNodes.length; index < cache; index++) {
const parsedJSONTransformNode = parsedData.transformNodes[index];
const parsedTransformNode = TransformNode.Parse(parsedJSONTransformNode, scene, rootUrl);
loadedTransformNodes.push(parsedTransformNode);
parsedIdToNodeMap.set(parsedTransformNode._waitingParsedUniqueId, parsedTransformNode);
parsedTransformNode._waitingParsedUniqueId = null;
}
}
if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
const loadedSkeletonsIds = [];
const loadedMaterialsIds = [];
const loadedMaterialsUniqueIds = [];
const loadedMorphTargetManagerIds = [];
for (let index = 0, cache = parsedData.meshes.length; index < cache; index++) {
const parsedMesh = parsedData.meshes[index];
if (meshesNames === null || IsDescendantOf(parsedMesh, meshesNames, hierarchyIds)) {
if (meshesNames !== null) {
// Remove found mesh name from list.
delete meshesNames[meshesNames.indexOf(parsedMesh.name)];
}
//Geometry?
if (parsedMesh.geometryId !== undefined && parsedMesh.geometryId !== null) {
//does the file contain geometries?
if (parsedData.geometries !== undefined && parsedData.geometries !== null) {
//find the correct geometry and add it to the scene
let found = false;
const geoms = ["boxes", "spheres", "cylinders", "toruses", "grounds", "planes", "torusKnots", "vertexData"];
for (const geometryType of geoms) {
if (!parsedData.geometries[geometryType] || !Array.isArray(parsedData.geometries[geometryType])) {
continue;
}
const geom = parsedData.geometries[geometryType];
for (const parsedGeometryData of geom) {
if (parsedGeometryData.id === parsedMesh.geometryId) {
switch (geometryType) {
case "vertexData":
Geometry.Parse(parsedGeometryData, scene, rootUrl);
break;
}
found = true;
break;
}
}
if (found) {
break;
}
}
if (found === false) {
Logger.Warn("Geometry not found for mesh " + parsedMesh.id);
}
}
}
// Material ?
if (parsedMesh.materialUniqueId || parsedMesh.materialId) {
// if we have a unique ID, look up and store in loadedMaterialsUniqueIds, else use loadedMaterialsIds
const materialArray = parsedMesh.materialUniqueId ? loadedMaterialsUniqueIds : loadedMaterialsIds;
let materialFound = materialArray.indexOf(parsedMesh.materialUniqueId || parsedMesh.materialId) !== -1;
if (materialFound === false && parsedData.multiMaterials !== undefined && parsedData.multiMaterials !== null) {
// Loads a submaterial of a multimaterial
const loadSubMaterial = (subMatId, predicate) => {
materialArray.push(subMatId);
const mat = ParseMaterialByPredicate(predicate, parsedData, scene, rootUrl);
if (mat && mat.material) {
TempMaterialIndexContainer[mat.parsedMaterial.uniqueId || mat.parsedMaterial.id] = mat.material;
log += "\n\tMaterial " + mat.material.toString(fullDetails);
}
};
for (let multimatIndex = 0, multimatCache = parsedData.multiMaterials.length; multimatIndex < multimatCache; multimatIndex++) {
const parsedMultiMaterial = parsedData.multiMaterials[multimatIndex];
if ((parsedMesh.materialUniqueId && parsedMultiMaterial.uniqueId === parsedMesh.materialUniqueId) ||
parsedMultiMaterial.id === parsedMesh.materialId) {
if (parsedMultiMaterial.materialsUniqueIds) {
// if the materials inside the multimat are stored by unique id
for (const subMatId of parsedMultiMaterial.materialsUniqueIds) {
loadSubMaterial(subMatId, (parsedMaterial) => parsedMaterial.uniqueId === subMatId);
}
}
else {
// if the mats are stored by id instead
for (const subMatId of parsedMultiMaterial.materials) {
loadSubMaterial(subMatId, (parsedMaterial) => parsedMaterial.id === subMatId);
}
}
materialArray.push(parsedMultiMaterial.uniqueId || parsedMultiMaterial.id);
const mmat = MultiMaterial.ParseMultiMaterial(parsedMultiMaterial, scene);
TempMaterialIndexContainer[parsedMultiMaterial.uniqueId || parsedMultiMaterial.id] = mmat;
if (mmat) {
materialFound = true;
log += "\n\tMulti-Material " + mmat.toString(fullDetails);
}
break;
}
}
}
if (materialFound === false) {
materialArray.push(parsedMesh.materialUniqueId || parsedMesh.materialId);
const mat = ParseMaterialByPredicate((parsedMaterial) => (parsedMesh.materialUniqueId && parsedMaterial.uniqueId === parsedMesh.materialUniqueId) || parsedMaterial.id === parsedMesh.materialId, parsedData, scene, rootUrl);
if (!mat || !mat.material) {
Logger.Warn("Material not found for mesh " + parsedMesh.id);
}
else {
TempMaterialIndexContainer[mat.parsedMaterial.uniqueId || mat.parsedMaterial.id] = mat.material;
log += "\n\tMaterial " + mat.material.toString(fullDetails);
}
}
}
// Skeleton ?
if (parsedMesh.skeletonId !== null &&
parsedMesh.skeletonId !== undefined &&
parsedData.skeletonId !== -1 &&
parsedData.skeletons !== undefined &&
parsedData.skeletons !== null) {
const skeletonAlreadyLoaded = loadedSkeletonsIds.indexOf(parsedMesh.skeletonId) > -1;
if (!skeletonAlreadyLoaded) {
for (let skeletonIndex = 0, skeletonCache = parsedData.skeletons.length; skeletonIndex < skeletonCache; skeletonIndex++) {
const parsedSkeleton = parsedData.skeletons[skeletonIndex];
if (parsedSkeleton.id === parsedMesh.skeletonId) {
const skeleton = Skeleton.Parse(parsedSkeleton, scene);
skeletons.push(skeleton);
loadedSkeletonsIds.push(parsedSkeleton.id);
log += "\n\tSkeleton " + skeleton.toString(fullDetails);
}
}
}
}
// Morph targets ?
if (parsedMesh.morphTargetManagerId > -1 && parsedData.morphTargetManagers !== undefined && parsedData.morphTargetManagers !== null) {
const morphTargetManagerAlreadyLoaded = loadedMorphTargetManagerIds.indexOf(parsedMesh.morphTargetManagerId) > -1;
if (!morphTargetManagerAlreadyLoaded) {
for (let morphTargetManagerIndex = 0; morphTargetManagerIndex < parsedData.morphTargetManagers.length; morphTargetManagerIndex++) {
const parsedManager = parsedData.morphTargetManagers[morphTargetManagerIndex];
if (parsedManager.id === parsedMesh.morphTargetManagerId) {
const morphTargetManager = MorphTargetManager.Parse(parsedManager, scene);
TempMorphTargetManagerIndexContainer[parsedManager.id] = morphTargetManager;
loadedMorphTargetManagerIds.push(parsedManager.id);
log += "\nMorph target manager" + morphTargetManager.toString();
}
}
}
}
const mesh = Mesh.Parse(parsedMesh, scene, rootUrl);
meshes.push(mesh);
parsedIdToNodeMap.set(mesh._waitingParsedUniqueId, mesh);
mesh._waitingParsedUniqueId = null;
log += "\n\tMesh " + mesh.toString(fullDetails);
}
}
// link multimats with materials
for (const multimat of scene.multiMaterials) {
for (const subMaterial of multimat._waitingSubMaterialsUniqueIds) {
multimat.subMaterials.push(FindMaterial(subMaterial, scene));
}
multimat._waitingSubMaterialsUniqueIds = [];
}
// link meshes with materials
for (const mesh of scene.meshes) {
if (mesh._waitingMaterialId !== null) {
mesh.material = FindMaterial(mesh._waitingMaterialId, scene);
mesh._waitingMaterialId = null;
}
}
// link meshes with morph target managers
for (const mesh of scene.meshes) {
if (mesh._waitingMorphTargetManagerId !== null) {
mesh.morphTargetManager = TempMorphTargetManagerIndexContainer[mesh._waitingMorphTargetManagerId];
mesh._waitingMorphTargetManagerId = null;
}
}
// Connecting parents and lods
for (let index = 0, cache = scene.transformNodes.length; index < cache; index++) {
const transformNode = scene.transformNodes[index];
if (transformNode._waitingParentId !== null) {
let parent = parsedIdToNodeMap.get(parseInt(transformNode._waitingParentId)) || null;
if (parent === null) {
parent = scene.getLastEntryById(transformNode._waitingParentId);
}
let parentNode = parent;
if (transformNode._waitingParentInstanceIndex) {
parentNode = parent.instances[parseInt(transformNode._waitingParentInstanceIndex)];
transformNode._waitingParentInstanceIndex = null;
}
transformNode.parent = parentNode;
transformNode._waitingParentId = null;
}
}
let currentMesh;
for (let index = 0, cache = scene.meshes.length; index < cache; index++) {
currentMesh = scene.meshes[index];
if (currentMesh._waitingParentId) {
let parent = parsedIdToNodeMap.get(parseInt(currentMesh._waitingParentId)) || null;
if (parent === null) {
parent = scene.getLastEntryById(currentMesh._waitingParentId);
}
let parentNode = parent;
if (currentMesh._waitingParentInstanceIndex) {
parentNode = parent.instances[parseInt(currentMesh._waitingParentInstanceIndex)];
currentMesh._waitingParentInstanceIndex = null;
}
currentMesh.parent = parentNode;
currentMesh._waitingParentId = null;
}
if (currentMesh._waitingData.lods) {
LoadDetailLevels(scene, currentMesh);
}
}
// Remove unused transform nodes
for (const transformNode of loadedTransformNodes) {
const childMeshes = transformNode.getChildMeshes(false);
if (!childMeshes.length) {
transformNode.dispose();
}
}
// link skeleton transform nodes
for (let index = 0, cache = scene.skeletons.length; index < cache; index++) {
const skeleton = scene.skeletons[index];
if (skeleton._hasWaitingData) {
if (skeleton.bones != null) {
for (const bone of skeleton.bones) {
if (bone._waitingTransformNodeId) {
const linkTransformNode = scene.getLastEntryById(bone._waitingTransformNodeId);
if (linkTransformNode) {
bone.linkTransformNode(linkTransformNode);
}
bone._waitingTransformNodeId = null;
}
}
}
skeleton._hasWaitingData = null;
}
}
// freeze and compute world matrix application
for (let index = 0, cache = scene.meshes.length; index < cache; index++) {
currentMesh = scene.meshes[index];
if (currentMesh._waitingData.freezeWorldMatrix) {
currentMesh.freezeWorldMatrix();
currentMesh._waitingData.freezeWorldMatrix = null;
}
else {
currentMesh.computeWorldMatrix(true);
}
}
}
// Particles
if (parsedData.particleSystems !== undefined && parsedData.particleSystems !== null) {
const parser = GetIndividualParser(SceneComponentConstants.NAME_PARTICLESYSTEM);
if (parser) {
for (let index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
const parsedParticleSystem = parsedData.particleSystems[index];
if (hierarchyIds.indexOf(parsedParticleSystem.emitterId) !== -1) {
particleSystems.push(parser(parsedParticleSystem, scene, rootUrl));
}
}
}
}
for (const g of scene.geometries) {
g._loadedUniqueId = "";
}
return true;
}
catch (err) {
const msg = logOperation("importMesh", parsedData ? parsedData.producer : "Unknown") + log;
if (onError) {
onError(msg, err);
}
else {
Logger.Log(msg);
throw err;
}
}
finally {
if (log !== null && SceneLoader.loggingLevel !== SceneLoader.NO_LOGGING) {
Logger.Log(logOperation("importMesh", parsedData ? parsedData.producer : "Unknown") + (SceneLoader.loggingLevel !== SceneLoader.MINIMAL_LOGGING ? log : ""));
}
TempMaterialIndexContainer = {};
TempMorphTargetManagerIndexContainer = {};
}
return false;
},
load: (scene, data, rootUrl, onError) => {
// Entire method running in try block, so ALWAYS logs as far as it got, only actually writes details
// when SceneLoader.debugLogging = true (default), or exception encountered.
// Everything stored in var log instead of writing separate lines to support only writing in exception,
// and avoid problems with multiple concurrent .babylon loads.
let log = "importScene has failed JSON parse";
try {
// eslint-disable-next-line no-var
var parsedData = JSON.parse(data);
log = "";
// Scene
if (parsedData.useDelayedTextureLoading !== undefined && parsedData.useDelayedTextureLoading !== null) {
scene.useDelayedTextureLoading = parsedData.useDelayedTextureLoading && !SceneLoader.ForceFullSceneLoadingForIncremental;
}
if (parsedData.autoClear !== undefined && parsedData.autoClear !== null) {
scene.autoClear = parsedData.autoClear;
}
if (parsedData.clearColor !== undefined && parsedData.clearColor !== null) {
s