486 lines
13 KiB
JavaScript
486 lines
13 KiB
JavaScript
OV.ThreeLoader = class
|
|
{
|
|
constructor ()
|
|
{
|
|
|
|
}
|
|
|
|
GetExtension ()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
GetExternalLibraries ()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
CreateLoader (manager)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
EnumerateMeshes (loadedObject, processor)
|
|
{
|
|
|
|
}
|
|
|
|
GetUpDirection ()
|
|
{
|
|
return null;
|
|
}
|
|
};
|
|
|
|
OV.ThreeLoaderFbx = class extends OV.ThreeLoader
|
|
{
|
|
constructor ()
|
|
{
|
|
super ();
|
|
}
|
|
|
|
GetExtension ()
|
|
{
|
|
return 'fbx';
|
|
}
|
|
|
|
GetExternalLibraries ()
|
|
{
|
|
return [
|
|
'three_loaders/fflate.min.js',
|
|
'three_loaders/TGALoader.js',
|
|
'three_loaders/FBXLoader.js'
|
|
];
|
|
}
|
|
|
|
CreateLoader (manager)
|
|
{
|
|
manager.addHandler (/\.tga$/i, new THREE.TGALoader (manager));
|
|
return new THREE.FBXLoader (manager);
|
|
}
|
|
|
|
EnumerateMeshes (loadedObject, processor)
|
|
{
|
|
loadedObject.traverse ((child) => {
|
|
if (child.isMesh) {
|
|
processor (child);
|
|
}
|
|
});
|
|
}
|
|
|
|
GetUpDirection ()
|
|
{
|
|
return OV.Direction.Y;
|
|
}
|
|
};
|
|
|
|
OV.ThreeLoaderDae = class extends OV.ThreeLoader
|
|
{
|
|
constructor ()
|
|
{
|
|
super ();
|
|
}
|
|
|
|
GetExtension ()
|
|
{
|
|
return 'dae';
|
|
}
|
|
|
|
GetExternalLibraries ()
|
|
{
|
|
return [
|
|
'three_loaders/TGALoader.js',
|
|
'three_loaders/ColladaLoader.js'
|
|
];
|
|
}
|
|
|
|
CreateLoader (manager)
|
|
{
|
|
manager.addHandler (/\.tga$/i, new THREE.TGALoader (manager));
|
|
return new THREE.ColladaLoader (manager);
|
|
}
|
|
|
|
EnumerateMeshes (loadedObject, processor)
|
|
{
|
|
loadedObject.scene.traverse ((child) => {
|
|
if (child.isMesh) {
|
|
processor (child);
|
|
}
|
|
});
|
|
}
|
|
|
|
GetUpDirection ()
|
|
{
|
|
return OV.Direction.Y;
|
|
}
|
|
};
|
|
|
|
OV.ThreeLoaderVrml = class extends OV.ThreeLoader
|
|
{
|
|
constructor ()
|
|
{
|
|
super ();
|
|
}
|
|
|
|
GetExtension ()
|
|
{
|
|
return 'wrl';
|
|
}
|
|
|
|
GetExternalLibraries ()
|
|
{
|
|
return [
|
|
'three_loaders/chevrotain.min.js',
|
|
'three_loaders/VRMLLoader.js'
|
|
];
|
|
}
|
|
|
|
CreateLoader (manager)
|
|
{
|
|
return new THREE.VRMLLoader (manager);
|
|
}
|
|
|
|
EnumerateMeshes (loadedObject, processor)
|
|
{
|
|
loadedObject.traverse ((child) => {
|
|
if (child.isMesh) {
|
|
let needToProcess = true;
|
|
if (Array.isArray (child.material)) {
|
|
for (let i = 0; i < child.material.length; i++) {
|
|
if (child.material[i].side === THREE.BackSide) {
|
|
needToProcess = false;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
needToProcess = (child.material.side !== THREE.BackSide);
|
|
}
|
|
if (needToProcess) {
|
|
processor (child);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
GetUpDirection ()
|
|
{
|
|
return OV.Direction.Y;
|
|
}
|
|
};
|
|
|
|
OV.ThreeLoader3mf = class extends OV.ThreeLoader
|
|
{
|
|
constructor ()
|
|
{
|
|
super ();
|
|
}
|
|
|
|
GetExtension ()
|
|
{
|
|
return '3mf';
|
|
}
|
|
|
|
GetExternalLibraries ()
|
|
{
|
|
return [
|
|
'three_loaders/fflate.min.js',
|
|
'three_loaders/3MFLoader.js'
|
|
];
|
|
}
|
|
|
|
CreateLoader (manager)
|
|
{
|
|
return new THREE.ThreeMFLoader (manager);
|
|
}
|
|
|
|
EnumerateMeshes (loadedObject, processor)
|
|
{
|
|
loadedObject.traverse ((child) => {
|
|
if (child.isMesh) {
|
|
processor (child);
|
|
}
|
|
});
|
|
}
|
|
|
|
GetUpDirection ()
|
|
{
|
|
return OV.Direction.Z;
|
|
}
|
|
};
|
|
|
|
OV.ThreeImporter = class extends OV.ImporterBase
|
|
{
|
|
constructor ()
|
|
{
|
|
super ();
|
|
this.loaders = [
|
|
new OV.ThreeLoaderFbx (),
|
|
new OV.ThreeLoaderDae (),
|
|
new OV.ThreeLoaderVrml (),
|
|
new OV.ThreeLoader3mf ()
|
|
];
|
|
}
|
|
|
|
CanImportExtension (extension)
|
|
{
|
|
for (let i = 0; i < this.loaders.length; i++) {
|
|
let loader = this.loaders[i];
|
|
if (loader.GetExtension () === extension) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
GetKnownFileFormats ()
|
|
{
|
|
let result = {};
|
|
for (let i = 0; i < this.loaders.length; i++) {
|
|
let loader = this.loaders[i];
|
|
result[loader.GetExtension ()] = OV.FileFormat.Binary;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
GetUpDirection ()
|
|
{
|
|
return this.loader.GetUpDirection ();
|
|
}
|
|
|
|
ClearContent ()
|
|
{
|
|
this.loader = null;
|
|
}
|
|
|
|
ResetContent ()
|
|
{
|
|
this.loader = null;
|
|
}
|
|
|
|
ImportContent (fileContent, onFinish)
|
|
{
|
|
async function LoadLibraries (libraries, onFinish, onError)
|
|
{
|
|
try {
|
|
for (let i = 0; i < libraries.length; i++) {
|
|
await OV.LoadExternalLibrary (libraries[i]);
|
|
}
|
|
} catch (err) {
|
|
onError ();
|
|
}
|
|
onFinish ();
|
|
}
|
|
|
|
this.loader = this.FindLoader ();
|
|
if (this.loader === null) {
|
|
onFinish ();
|
|
return;
|
|
}
|
|
|
|
const libraries = this.loader.GetExternalLibraries ();
|
|
if (libraries === null) {
|
|
onFinish ();
|
|
return;
|
|
}
|
|
|
|
LoadLibraries (libraries, () => {
|
|
this.LoadModel (fileContent, onFinish);
|
|
}, () => {
|
|
onFinish ();
|
|
});
|
|
}
|
|
|
|
FindLoader ()
|
|
{
|
|
for (let i = 0; i < this.loaders.length; i++) {
|
|
let loader = this.loaders[i];
|
|
if (loader.GetExtension () === this.extension) {
|
|
return loader;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
LoadModel (fileContent, onFinish)
|
|
{
|
|
let loadedObject = null;
|
|
let externalFileNames = {};
|
|
let loadingManager = new THREE.LoadingManager (() => {
|
|
if (loadedObject !== null) {
|
|
this.OnThreeObjectsLoaded (loadedObject, externalFileNames, onFinish);
|
|
}
|
|
});
|
|
|
|
const mainFileUrl = OV.CreateObjectUrl (fileContent);
|
|
loadingManager.setURLModifier ((url) => {
|
|
if (url === mainFileUrl) {
|
|
return url;
|
|
}
|
|
if (url.startsWith ('blob:')) {
|
|
const name = OV.GetFileName (url);
|
|
const extension = OV.GetFileExtension (url);
|
|
if (extension.length > 0) {
|
|
const buffer = this.callbacks.getFileBuffer (url);
|
|
if (buffer !== null) {
|
|
let objectUrl = OV.CreateObjectUrl (buffer);
|
|
externalFileNames[objectUrl] = name;
|
|
return objectUrl;
|
|
}
|
|
}
|
|
}
|
|
return url;
|
|
});
|
|
|
|
const threeLoader = this.loader.CreateLoader (loadingManager);
|
|
if (threeLoader === null) {
|
|
onFinish ();
|
|
return;
|
|
}
|
|
|
|
threeLoader.load (mainFileUrl,
|
|
(object) => {
|
|
loadedObject = object;
|
|
},
|
|
() => {
|
|
},
|
|
(err) => {
|
|
this.SetError ();
|
|
this.SetMessage (err);
|
|
onFinish ();
|
|
}
|
|
);
|
|
}
|
|
|
|
OnThreeObjectsLoaded (loadedObject, externalFileNames, onFinish)
|
|
{
|
|
function ConvertThreeMaterialToMaterial (threeMaterial, externalFileNames)
|
|
{
|
|
function SetColor (color, threeColor)
|
|
{
|
|
color.Set (
|
|
parseInt (threeColor.r * 255.0, 10),
|
|
parseInt (threeColor.g * 255.0, 10),
|
|
parseInt (threeColor.b * 255.0, 10)
|
|
);
|
|
}
|
|
|
|
function CreateTexture (threeMap, externalFileNames)
|
|
{
|
|
function GetDataUrl (img)
|
|
{
|
|
if (img.data !== undefined && img.data !== null) {
|
|
let imageData = new ImageData (img.width, img.height);
|
|
let imageSize = img.width * img.height * 4;
|
|
for (let i = 0; i < imageSize; i++) {
|
|
imageData.data[i] = img.data[i];
|
|
}
|
|
return THREE.ImageUtils.getDataURL (imageData);
|
|
} else {
|
|
return THREE.ImageUtils.getDataURL (threeMap.image);
|
|
}
|
|
}
|
|
|
|
if (threeMap === undefined || threeMap === null) {
|
|
return null;
|
|
}
|
|
|
|
if (threeMap.image === undefined || threeMap.image === null) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const dataUrl = GetDataUrl (threeMap.image);
|
|
const base64Buffer = OV.Base64DataURIToArrayBuffer (dataUrl);
|
|
let texture = new OV.TextureMap ();
|
|
let textureName = externalFileNames[threeMap.image.src];
|
|
if (textureName === undefined) {
|
|
textureName = 'Embedded_' + threeMap.id.toString () + '.' + OV.GetFileExtensionFromMimeType (base64Buffer.mimeType);
|
|
}
|
|
texture.name = textureName;
|
|
texture.url = dataUrl;
|
|
texture.buffer = base64Buffer.buffer;
|
|
texture.rotation = threeMap.rotation;
|
|
texture.offset.x = threeMap.offset.x;
|
|
texture.offset.y = threeMap.offset.y;
|
|
texture.scale.x = threeMap.repeat.x;
|
|
texture.scale.y = threeMap.repeat.y;
|
|
return texture;
|
|
} catch (err) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
let material = new OV.Material (OV.MaterialType.Phong);
|
|
material.name = threeMaterial.name;
|
|
SetColor (material.color, threeMaterial.color);
|
|
material.opacity = threeMaterial.opacity;
|
|
material.transparent = threeMaterial.transparent;
|
|
material.alphaTest = threeMaterial.alphaTest;
|
|
if (threeMaterial.type === 'MeshPhongMaterial') {
|
|
SetColor (material.specular, threeMaterial.specular);
|
|
material.shininess = threeMaterial.shininess / 100.0;
|
|
}
|
|
material.diffuseMap = CreateTexture (threeMaterial.map, externalFileNames);
|
|
material.normalMap = CreateTexture (threeMaterial.normalMap, externalFileNames);
|
|
material.bumpMap = CreateTexture (threeMaterial.bumpMap, externalFileNames);
|
|
|
|
return material;
|
|
}
|
|
|
|
function FindMatchingMaterialIndex (model, threeMaterial, materialIdToIndex, externalFileNames)
|
|
{
|
|
let index = materialIdToIndex[threeMaterial.id];
|
|
if (index !== undefined) {
|
|
return index;
|
|
}
|
|
let material = ConvertThreeMaterialToMaterial (threeMaterial, externalFileNames);
|
|
index = model.AddMaterial (material);
|
|
materialIdToIndex[threeMaterial.id] = index;
|
|
return index;
|
|
}
|
|
|
|
let materialIdToIndex = {};
|
|
this.loader.EnumerateMeshes (loadedObject, (child) => {
|
|
let materialIndex = null;
|
|
let mesh = null;
|
|
if (Array.isArray (child.material)) {
|
|
mesh = OV.ConvertThreeGeometryToMesh (child.geometry, null);
|
|
if (child.geometry.attributes.color === undefined || child.geometry.attributes.color === null) {
|
|
let materialIndices = [];
|
|
for (let i = 0; i < child.material.length; i++) {
|
|
let material = child.material[i];
|
|
materialIndices.push (FindMatchingMaterialIndex (this.model, material, materialIdToIndex, externalFileNames));
|
|
}
|
|
for (let i = 0; i < child.geometry.groups.length; i++) {
|
|
let group = child.geometry.groups[i];
|
|
for (let j = group.start / 3; j < group.start / 3 + group.count / 3; j++) {
|
|
let triangle = mesh.GetTriangle (j);
|
|
triangle.SetMaterial (materialIndices[group.materialIndex]);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
materialIndex = FindMatchingMaterialIndex (this.model, child.material, materialIdToIndex, externalFileNames);
|
|
mesh = OV.ConvertThreeGeometryToMesh (child.geometry, materialIndex);
|
|
}
|
|
if (child.name !== undefined && child.name !== null) {
|
|
mesh.SetName (child.name);
|
|
}
|
|
child.updateWorldMatrix (true, true);
|
|
if (child.matrixWorld !== undefined && child.matrixWorld !== null) {
|
|
const matrix = new OV.Matrix (child.matrixWorld.elements);
|
|
const transformation = new OV.Transformation (matrix);
|
|
const determinant = matrix.Determinant ();
|
|
const mirrorByX = OV.IsNegative (determinant);
|
|
OV.TransformMesh (mesh, transformation);
|
|
if (mirrorByX) {
|
|
OV.FlipMeshTrianglesOrientation (mesh);
|
|
}
|
|
}
|
|
this.model.AddMesh (mesh);
|
|
child.geometry.dispose ();
|
|
});
|
|
|
|
onFinish ();
|
|
}
|
|
};
|