Skip to content

Commit

Permalink
fix(GLTFImporter): add support for multiple primitives
Browse files Browse the repository at this point in the history
  • Loading branch information
daker authored and finetjul committed Nov 26, 2024
1 parent 9ab3172 commit 34d5078
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 49 deletions.
2 changes: 1 addition & 1 deletion Sources/IO/Geometry/GLTFImporter/Animations.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ function createAnimationSampler(glTFSampler) {
result = cubicSplineInterpolate(path, t0, t1, i0, i1, time);
break;
default:
throw new Error(
vtkWarningMacro(
`Unknown interpolation method: ${glTFSampler.interpolation}`
);
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/IO/Geometry/GLTFImporter/Parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ class GLTFParser {
if (mesh.primitives) {
mesh.primitives = mesh.primitives.map((primitive, idx) => {
const attributes = primitive.attributes;
primitive.name = `submesh-${idx}`;
primitive.name = `primitive-${idx}`;
primitive.attributes = {};
for (const attribute in attributes) {
const attr = SEMANTIC_ATTRIBUTE_MAP[attribute];
Expand Down
107 changes: 62 additions & 45 deletions Sources/IO/Geometry/GLTFImporter/Reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,16 @@ async function parseGLTF(gltf, options) {
}

/**
* Creates VTK polydata from a GLTF mesh
* @param {GLTFMesh} mesh - The GLTF mesh
* Creates VTK polydata from a GLTF mesh primitive
* @param {GLTFPrimitive} primitive - The GLTF mesh primitive
* @returns {vtkPolyData} The created VTK polydata
*/
async function createPolyDataFromGLTFMesh(mesh) {
const primitive = mesh.primitives[0]; // For simplicity, we'll just use the first primitive

async function createPolyDataFromGLTFMesh(primitive) {
if (!primitive || !primitive.attributes) {
vtkWarningMacro('Mesh has no position data, skipping');
vtkWarningMacro('Primitive has no position data, skipping');
return null;
}

const mode = primitive.mode;

if (primitive.extensions?.KHR_draco_mesh_compression) {
return handleKHRDracoMeshCompression(
primitive.extensions.KHR_draco_mesh_compression
Expand Down Expand Up @@ -136,10 +132,10 @@ async function createPolyDataFromGLTFMesh(mesh) {
});

// Handle indices if available
if (primitive.indices !== undefined) {
if (primitive.indices != null) {
const indices = primitive.indices.value;
const nCells = indices.length - 2;
switch (mode) {
switch (primitive.mode) {
case MODES.GL_LINE_STRIP:
case MODES.GL_TRIANGLE_STRIP:
case MODES.GL_LINE_LOOP:
Expand All @@ -154,7 +150,7 @@ async function createPolyDataFromGLTFMesh(mesh) {
}
}

switch (mode) {
switch (primitive.mode) {
case MODES.GL_TRIANGLES:
case MODES.GL_TRIANGLE_FAN:
polyData.setPolys(cells);
Expand All @@ -179,7 +175,7 @@ async function createPolyDataFromGLTFMesh(mesh) {

/**
* Creates a VTK property from a GLTF material
* @param {*} model - The vtk model object
* @param {object} model - The vtk model object
* @param {GLTFMaterial} material - The GLTF material
* @param {vtkActor} actor - The VTK actor
*/
Expand All @@ -191,13 +187,13 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {
const property = actor.getProperty();
const pbr = material.pbrMetallicRoughness;

if (pbr !== undefined) {
if (pbr != null) {
if (
!pbr?.metallicFactor ||
pbr?.metallicFactor <= 0 ||
pbr?.metallicFactor >= 1
) {
vtkWarningMacro(
vtkDebugMacro(
'Invalid material.pbrMetallicRoughness.metallicFactor value. Using default value instead.'
);
} else metallicFactor = pbr.metallicFactor;
Expand All @@ -206,14 +202,14 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {
pbr?.roughnessFactor <= 0 ||
pbr?.roughnessFactor >= 1
) {
vtkWarningMacro(
vtkDebugMacro(
'Invalid material.pbrMetallicRoughness.roughnessFactor value. Using default value instead.'
);
} else roughnessFactor = pbr.roughnessFactor;

const color = pbr.baseColorFactor;

if (color !== undefined) {
if (color != null) {
property.setDiffuseColor(color[0], color[1], color[2]);
property.setOpacity(color[3]);
}
Expand All @@ -226,7 +222,7 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {
const extensions = pbr.baseColorTexture.extensions;
const tex = pbr.baseColorTexture.texture;

if (tex.extensions !== undefined) {
if (tex.extensions != null) {
const extensionsNames = Object.keys(tex.extensions);
extensionsNames.forEach((extensionName) => {
// TODO: Handle KHR_texture_basisu extension
Expand Down Expand Up @@ -305,7 +301,7 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {
property.setEmissionTexture(emissiveTex);

// Handle mutiple Uvs
if (material.emissiveTexture.texCoord !== undefined) {
if (material.emissiveTexture.texCoord != null) {
const pd = actor.getMapper().getInputData().getPointData();
pd.setActiveTCoords(`TEXCOORD_${material.emissiveTexture.texCoord}`);
}
Expand All @@ -324,14 +320,14 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {
);
property.setNormalTexture(normalTex);

if (material.normalTexture.scale !== undefined) {
if (material.normalTexture.scale != null) {
property.setNormalStrength(material.normalTexture.scale);
}
}
}

// Material extensions
if (material.extensions !== undefined) {
if (material.extensions != null) {
const extensionsNames = Object.keys(material.extensions);
extensionsNames.forEach((extensionName) => {
const extension = material.extensions[extensionName];
Expand Down Expand Up @@ -360,17 +356,17 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {

/**
* Handles primitive extensions
* @param {string} nodeId The GLTF node id
* @param {*} extensions The extensions object
* @param {*} model The vtk model object
* @param {GLTFNode} node The GLTF node
*/
function handlePrimitiveExtensions(extensions, model, node) {
function handlePrimitiveExtensions(nodeId, extensions, model) {
const extensionsNames = Object.keys(extensions);
extensionsNames.forEach((extensionName) => {
const extension = extensions[extensionName];
switch (extensionName) {
case 'KHR_materials_variants':
model.variantMappings.set(node.id, extension.mappings);
model.variantMappings.set(nodeId, extension.mappings);
break;
default:
vtkWarningMacro(`Unhandled extension: ${extensionName}`);
Expand All @@ -383,37 +379,47 @@ function handlePrimitiveExtensions(extensions, model, node) {
* @param {GLTFMesh} mesh - The GLTF mesh
* @returns {vtkActor} The created VTK actor
*/
async function createActorFromGTLFNode(model, node, worldMatrix) {
async function createActorFromGTLFNode(worldMatrix) {
const actor = vtkActor.newInstance();
const mapper = vtkMapper.newInstance();
mapper.setColorModeToDirectScalars();
actor.setMapper(mapper);
actor.setUserMatrix(worldMatrix);

if (node.mesh !== undefined) {
const polyData = await createPolyDataFromGLTFMesh(node.mesh);
mapper.setInputData(polyData);
const polydata = vtkPolyData.newInstance();
mapper.setInputData(polydata);
return actor;
}

/**
* Creates a VTK actor from a GLTF mesh
* @param {GLTFMesh} mesh - The GLTF mesh
* @returns {vtkActor} The created VTK actor
*/
async function createActorFromGTLFPrimitive(model, primitive, worldMatrix) {
const actor = vtkActor.newInstance();
const mapper = vtkMapper.newInstance();
mapper.setColorModeToDirectScalars();
actor.setMapper(mapper);
actor.setUserMatrix(worldMatrix);

const primitive = node.mesh.primitives[0]; // the first one for now
const polydata = await createPolyDataFromGLTFMesh(primitive);
mapper.setInputData(polydata);

// Support for materials
if (primitive.material !== undefined) {
await createPropertyFromGLTFMaterial(model, primitive.material, actor);
}
// Support for materials
if (primitive.material != null) {
await createPropertyFromGLTFMaterial(model, primitive.material, actor);
}

if (primitive.extensions !== undefined) {
handlePrimitiveExtensions(primitive.extensions, model, node);
}
} else {
const polyData = vtkPolyData.newInstance();
mapper.setInputData(polyData);
if (primitive.extensions != null) {
handlePrimitiveExtensions(`${primitive.name}`, primitive.extensions, model);
}

return actor;
}

/**
*
* Creates a GLTF animation object
* @param {GLTFAnimation} animation
* @returns
*/
Expand Down Expand Up @@ -443,7 +449,7 @@ function getTransformationMatrix(node) {
const scale = node.scale ?? vec3.fromValues(1.0, 1.0, 1.0);

const matrix =
node.matrix !== undefined
node.matrix != null
? mat4.clone(node.matrix)
: mat4.fromRotationTranslationScale(
mat4.create(),
Expand Down Expand Up @@ -475,13 +481,24 @@ async function processNode(
);

// Create actor for the current node
const actor = await createActorFromGTLFNode(model, node, worldMatrix);
if (actor) {
actor.setUserMatrix(worldMatrix);
if (node.mesh != null) {
const nodeActor = await createActorFromGTLFNode(worldMatrix);
if (parentActor) {
actor.setParentProp(parentActor);
nodeActor.setParentProp(parentActor);
}
model.actors.set(node.id, actor);
model.actors.set(`${node.id}`, nodeActor);

await Promise.all(
node.mesh.primitives.map(async (primitive, i) => {
const actor = await createActorFromGTLFPrimitive(
model,
primitive,
worldMatrix
);
actor.setParentProp(nodeActor);
model.actors.set(`${node.id}_${primitive.name}`, actor);
})
);
}

// Handle KHRLightsPunctual extension
Expand Down
2 changes: 1 addition & 1 deletion Sources/IO/Geometry/GLTFImporter/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export function createVTKTextureFromGLTFTexture(image, sampler, extensions) {
texture.setInterpolate(true);
}
} else {
texture.MipmapOn();
texture.setMipLevel(8);
texture.setInterpolate(true);
texture.setEdgeClamp(true);
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/IO/Geometry/GLTFImporter/example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const variantsModels = [
'MaterialsVariantsShoe',
'GlamVelvetSofa',
'SheenChair',
'AntiqueCamera',
];

const environmentTex = createTextureWithMipmap(
Expand Down Expand Up @@ -247,7 +248,7 @@ fetch(`${baseUrl}/${modelsFolder}/model-index.json`)
});

selectedModel = userParms.model || modelsNames[0];
const variants = Object.keys(modelsDictionary[selectedModel]);
const variants = Object.keys(modelsDictionary[selectedModel]).sort();

selectedFlavor = userParms.flavor || variants[0];
variants.forEach((variant) => {
Expand Down

0 comments on commit 34d5078

Please sign in to comment.