mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2025-12-11 20:15:30 +01:00
Merge pull request #166 from ryanhlewis/main
GLTF Multiple Materials Fix
This commit is contained in:
commit
0813e8ddae
@ -1,204 +1,170 @@
|
|||||||
import { parse } from '@loaders.gl/core';
|
import { parse } from '@loaders.gl/core';
|
||||||
import { GLTFLoader } from '@loaders.gl/gltf';
|
import { GLTFLoader } from '@loaders.gl/gltf';
|
||||||
|
|
||||||
import { RGBAColours, RGBAUtil } from '../colour';
|
import { RGBAColours, RGBAUtil } from '../colour';
|
||||||
import { LOC } from '../localiser';
|
import { LOC } from '../localiser';
|
||||||
import { MaterialMap, MaterialType, Mesh, Tri } from '../mesh';
|
import { MaterialMap, MaterialType, Mesh, Tri } from '../mesh';
|
||||||
import { StatusHandler } from '../status';
|
import { StatusHandler } from '../status';
|
||||||
import { UV } from '../util';
|
import { UV } from '../util';
|
||||||
import { AppError } from '../util/error_util';
|
import { AppError } from '../util/error_util';
|
||||||
import { Vector3 } from '../vector';
|
import { Vector3 } from '../vector';
|
||||||
import { IImporter } from './base_importer';
|
import { IImporter } from './base_importer';
|
||||||
|
|
||||||
export class GltfLoader extends IImporter {
|
export class GltfLoader extends IImporter {
|
||||||
public override import(file: File): Promise<Mesh> {
|
public override import(file: File): Promise<Mesh> {
|
||||||
StatusHandler.warning(LOC('import.gltf_experimental'));
|
StatusHandler.warning(LOC('import.gltf_experimental'));
|
||||||
|
|
||||||
return new Promise<Mesh>((resolve, reject) => {
|
return new Promise<Mesh>((resolve, reject) => {
|
||||||
parse(file, GLTFLoader, { loadImages: true })
|
parse(file, GLTFLoader, { loadImages: true })
|
||||||
.then((gltf: any) => {
|
.then((gltf: any) => {
|
||||||
resolve(this._handleGLTF(gltf));
|
resolve(this._handleGLTF(gltf));
|
||||||
})
|
})
|
||||||
.catch((err: any) => {
|
.catch((err: any) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleGLTF(gltf: any): Mesh {
|
private _handleGLTF(gltf: any): Mesh {
|
||||||
const meshVertices: Vector3[] = [];
|
const meshVertices: Vector3[] = [];
|
||||||
const meshNormals: Vector3[] = [];
|
const meshNormals: Vector3[] = [];
|
||||||
const meshTexcoords: UV[] = [];
|
const meshTexcoords: UV[] = [];
|
||||||
const meshTriangles: Tri[] = [];
|
const meshTriangles: Tri[] = [];
|
||||||
const meshMaterials: MaterialMap = new Map();
|
const meshMaterials: MaterialMap = new Map();
|
||||||
meshMaterials.set('NONE', {
|
|
||||||
type: MaterialType.solid,
|
meshMaterials.set('NONE', {
|
||||||
colour: RGBAUtil.copy(RGBAColours.WHITE),
|
type: MaterialType.solid,
|
||||||
needsAttention: false,
|
colour: RGBAUtil.copy(RGBAColours.WHITE),
|
||||||
canBeTextured: false,
|
needsAttention: false,
|
||||||
});
|
canBeTextured: false,
|
||||||
let maxIndex = 0;
|
});
|
||||||
|
|
||||||
Object.values(gltf.meshes).forEach((mesh: any) => {
|
let maxIndex = 0;
|
||||||
Object.values(mesh.primitives).forEach((primitive: any) => {
|
let materialIndex = 0; // New variable to create unique material identifiers
|
||||||
const attributes = primitive.attributes;
|
|
||||||
if (attributes.POSITION !== undefined) {
|
Object.values(gltf.meshes).forEach((mesh: any) => {
|
||||||
const positions = attributes.POSITION.value as Float32Array;
|
Object.values(mesh.primitives).forEach((primitive: any) => {
|
||||||
for (let i = 0; i < positions.length; i += 3) {
|
const attributes = primitive.attributes;
|
||||||
meshVertices.push(new Vector3(
|
|
||||||
positions[i + 0],
|
// Handling vertices
|
||||||
positions[i + 1],
|
if (attributes.POSITION !== undefined) {
|
||||||
positions[i + 2],
|
const positions = attributes.POSITION.value as Float32Array;
|
||||||
));
|
for (let i = 0; i < positions.length; i += 3) {
|
||||||
}
|
meshVertices.push(new Vector3(
|
||||||
}
|
positions[i + 0],
|
||||||
if (attributes.NORMAL !== undefined) {
|
positions[i + 1],
|
||||||
const normals = attributes.NORMAL.value as Float32Array;
|
positions[i + 2],
|
||||||
for (let i = 0; i < normals.length; i += 3) {
|
));
|
||||||
meshNormals.push(new Vector3(
|
}
|
||||||
normals[i + 0],
|
}
|
||||||
normals[i + 1],
|
|
||||||
normals[i + 2],
|
// Handling normals
|
||||||
));
|
if (attributes.NORMAL !== undefined) {
|
||||||
}
|
const normals = attributes.NORMAL.value as Float32Array;
|
||||||
}
|
for (let i = 0; i < normals.length; i += 3) {
|
||||||
if (attributes.TEXCOORD_0 !== undefined) {
|
meshNormals.push(new Vector3(
|
||||||
const texcoords = attributes.TEXCOORD_0.value as Float32Array;
|
normals[i + 0],
|
||||||
for (let i = 0; i < texcoords.length; i += 2) {
|
normals[i + 1],
|
||||||
meshTexcoords.push(new UV(
|
normals[i + 2],
|
||||||
texcoords[i + 0],
|
));
|
||||||
1.0 - texcoords[i + 1],
|
}
|
||||||
));
|
}
|
||||||
}
|
|
||||||
}
|
// Handling texture coordinates
|
||||||
// Material
|
if (attributes.TEXCOORD_0 !== undefined) {
|
||||||
let materialNameToUse = 'NONE';
|
const texcoords = attributes.TEXCOORD_0.value as Float32Array;
|
||||||
{
|
for (let i = 0; i < texcoords.length; i += 2) {
|
||||||
if (primitive.material) {
|
meshTexcoords.push(new UV(
|
||||||
const materialName = primitive.material.name;
|
texcoords[i + 0],
|
||||||
|
1.0 - texcoords[i + 1],
|
||||||
let materialMade = false;
|
));
|
||||||
|
}
|
||||||
const pbr = primitive.material.pbrMetallicRoughness;
|
}
|
||||||
if (pbr !== undefined) {
|
|
||||||
const diffuseTexture = pbr.baseColorTexture;
|
// Material
|
||||||
if (diffuseTexture !== undefined) {
|
let materialBaseName = 'NONE';
|
||||||
const imageData: Uint8Array = diffuseTexture.texture.source.bufferView.data;
|
if (primitive.material) {
|
||||||
const mimeType: string = diffuseTexture.texture.source.mimeType;
|
materialBaseName = primitive.material.name || 'Material';
|
||||||
|
}
|
||||||
try {
|
|
||||||
if (mimeType !== 'image/png' && mimeType !== 'image/jpeg') {
|
const materialNameToUse = materialBaseName + '_' + materialIndex; // Unique material identifier
|
||||||
StatusHandler.warning(LOC('import.unsupported_image_type', { file_name: diffuseTexture.texture.source.id, file_type: mimeType }));
|
materialIndex++; // Increment material index
|
||||||
throw new Error('Unsupported image type');
|
|
||||||
}
|
// Handling materials
|
||||||
|
if (primitive.material) {
|
||||||
const base64 = btoa(
|
const pbr = primitive.material.pbrMetallicRoughness;
|
||||||
imageData.reduce((data, byte) => data + String.fromCharCode(byte), ''),
|
if (pbr !== undefined) {
|
||||||
);
|
const diffuseTexture = pbr.baseColorTexture;
|
||||||
|
if (diffuseTexture !== undefined) {
|
||||||
meshMaterials.set(materialName, {
|
const imageData: Uint8Array = diffuseTexture.texture.source.bufferView.data;
|
||||||
type: MaterialType.textured,
|
const mimeType: string = diffuseTexture.texture.source.mimeType;
|
||||||
diffuse: {
|
|
||||||
filetype: mimeType === 'image/jpeg' ? 'jpg' : 'png',
|
try {
|
||||||
raw: (mimeType === 'image/jpeg' ? 'data:image/jpeg;base64,' : 'data:image/png;base64,') + base64,
|
if (mimeType !== 'image/png' && mimeType !== 'image/jpeg') {
|
||||||
},
|
StatusHandler.warning(LOC('import.unsupported_image_type', { file_name: diffuseTexture.texture.source.id, file_type: mimeType }));
|
||||||
extension: 'clamp',
|
throw new Error('Unsupported image type');
|
||||||
interpolation: 'linear',
|
}
|
||||||
needsAttention: false,
|
|
||||||
transparency: { type: 'None' },
|
const base64 = btoa(
|
||||||
});
|
imageData.reduce((data, byte) => data + String.fromCharCode(byte), ''),
|
||||||
} catch {
|
);
|
||||||
meshMaterials.set(materialName, {
|
|
||||||
type: MaterialType.solid,
|
meshMaterials.set(materialNameToUse, {
|
||||||
colour: RGBAUtil.copy(RGBAColours.WHITE),
|
type: MaterialType.textured,
|
||||||
needsAttention: false,
|
diffuse: {
|
||||||
canBeTextured: true,
|
filetype: mimeType === 'image/jpeg' ? 'jpg' : 'png',
|
||||||
});
|
raw: (mimeType === 'image/jpeg' ? 'data:image/jpeg;base64,' : 'data:image/png;base64,') + base64,
|
||||||
}
|
},
|
||||||
|
extension: 'clamp',
|
||||||
|
interpolation: 'linear',
|
||||||
/*
|
needsAttention: false,
|
||||||
|
transparency: { type: 'None' },
|
||||||
*/
|
});
|
||||||
|
} catch {
|
||||||
materialNameToUse = materialName;
|
meshMaterials.set(materialNameToUse, {
|
||||||
materialMade = true;
|
type: MaterialType.solid,
|
||||||
} else {
|
colour: RGBAUtil.copy(RGBAColours.WHITE),
|
||||||
const diffuseColour: (number[] | undefined) = pbr.baseColorFactor;
|
needsAttention: false,
|
||||||
|
canBeTextured: true,
|
||||||
if (diffuseColour !== undefined) {
|
});
|
||||||
meshMaterials.set(materialName, {
|
}
|
||||||
type: MaterialType.solid,
|
}
|
||||||
colour: {
|
}
|
||||||
r: diffuseColour[0],
|
}
|
||||||
g: diffuseColour[1],
|
|
||||||
b: diffuseColour[2],
|
// Indices
|
||||||
a: diffuseColour[3],
|
const indices = primitive.indices.value as Uint16Array;
|
||||||
},
|
for (let i = 0; i < indices.length / 3; ++i) {
|
||||||
needsAttention: false,
|
meshTriangles.push({
|
||||||
canBeTextured: false,
|
material: materialNameToUse,
|
||||||
});
|
positionIndices: {
|
||||||
}
|
x: maxIndex + indices[i * 3 + 0],
|
||||||
|
y: maxIndex + indices[i * 3 + 1],
|
||||||
materialNameToUse = materialName;
|
z: maxIndex + indices[i * 3 + 2],
|
||||||
materialMade = true;
|
},
|
||||||
}
|
texcoordIndices: {
|
||||||
}
|
x: maxIndex + indices[i * 3 + 0],
|
||||||
|
y: maxIndex + indices[i * 3 + 1],
|
||||||
const emissiveColour: (number[] | undefined) = primitive.material.pbr;
|
z: maxIndex + indices[i * 3 + 2],
|
||||||
if (!materialMade && emissiveColour !== undefined) {
|
},
|
||||||
meshMaterials.set(materialName, {
|
});
|
||||||
type: MaterialType.solid,
|
}
|
||||||
colour: {
|
|
||||||
r: emissiveColour[0],
|
let localMax = 0;
|
||||||
g: emissiveColour[1],
|
for (let i = 0; i < indices.length; ++i) {
|
||||||
b: emissiveColour[2],
|
localMax = Math.max(localMax, indices[i]);
|
||||||
a: 1.0,
|
}
|
||||||
},
|
|
||||||
needsAttention: false,
|
maxIndex += localMax + 1;
|
||||||
canBeTextured: false,
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
materialNameToUse = materialName;
|
return new Mesh(
|
||||||
materialMade = true;
|
meshVertices,
|
||||||
}
|
meshNormals,
|
||||||
}
|
meshTexcoords,
|
||||||
}
|
meshTriangles,
|
||||||
// Indices
|
meshMaterials,
|
||||||
{
|
);
|
||||||
const indices = primitive.indices.value as Uint16Array;
|
}
|
||||||
for (let i = 0; i < indices.length / 3; ++i) {
|
}
|
||||||
meshTriangles.push({
|
|
||||||
material: materialNameToUse,
|
|
||||||
positionIndices: {
|
|
||||||
x: maxIndex + indices[i * 3 + 0],
|
|
||||||
y: maxIndex + indices[i * 3 + 1],
|
|
||||||
z: maxIndex + indices[i * 3 + 2],
|
|
||||||
},
|
|
||||||
texcoordIndices: {
|
|
||||||
x: maxIndex + indices[i * 3 + 0],
|
|
||||||
y: maxIndex + indices[i * 3 + 1],
|
|
||||||
z: maxIndex + indices[i * 3 + 2],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let localMax = 0;
|
|
||||||
for (let i = 0; i < indices.length; ++i) {
|
|
||||||
localMax = Math.max(localMax, indices[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
maxIndex += localMax + 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Mesh(
|
|
||||||
meshVertices,
|
|
||||||
meshNormals,
|
|
||||||
meshTexcoords,
|
|
||||||
meshTriangles,
|
|
||||||
meshMaterials,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user