MTLReader

Introduction

The vtkMTLReader aims to parse the MTL(Material Template Library file format
which is a companion file format to .OBJ that describes surface shading
(material) properties of objects within one or more .OBJ files.

Methods

applyMaterialToActor

Argument Type Required Description
name String Yes
actor vtkActor Yes

extend

Method used to decorate a given object (publicAPI+model) with vtkMTLReader characteristics.

Argument Type Required Description
publicAPI Yes object on which methods will be bounds (public)
model Yes object on which data structure will be bounds (protected)
initialValues IMTLReaderInitialValues No (default: {})

getBaseURL

getDataAccessHelper

getInterpolateTextures

getMaterial

Argument Type Required Description
name String Yes The name of the material.

getMaterialNames

getSplitGroup

getUrl

Get the url of the object to load.

invokeBusy

Argument Type Required Description
busy Boolean Yes

isBusy

listImages

loadData

Load the object data.

Argument Type Required Description
options IMTLReaderOptions No

newInstance

Method used to create a new instance of vtkMTLReader

Argument Type Required Description
initialValues IMTLReaderInitialValues No for pre-setting some of its content

onBusy

Argument Type Required Description
callback Yes

parseAsText

Parse data as text.

Argument Type Required Description
content String Yes The content to parse.

requestData

Argument Type Required Description
inData Yes
outData Yes

setDataAccessHelper

Argument Type Required Description
dataAccessHelper Yes

setImageSrc

Argument Type Required Description
imagePath Yes
src Yes

setInterpolateTextures

Argument Type Required Description
interpolateTextures Yes

setSplitGroup

Argument Type Required Description
splitGroup Yes

setUrl

Set the url of the object to load.

Argument Type Required Description
url String Yes the url of the object to load.
option IMTLReaderOptions No The MTL reader options.

Source

index.d.ts
import { vtkObject, vtkSubscription } from '../../../interfaces';

import vtkActor from '../../../Rendering/Core/Actor';
import HtmlDataAccessHelper from '../../Core/DataAccessHelper/HtmlDataAccessHelper';
import HttpDataAccessHelper from '../../Core/DataAccessHelper/HttpDataAccessHelper';
import JSZipDataAccessHelper from '../../Core/DataAccessHelper/JSZipDataAccessHelper';
import LiteHttpDataAccessHelper from '../../Core/DataAccessHelper/LiteHttpDataAccessHelper';

interface IMTLReaderOptions {
binary?: boolean;
compression?: string;
progressCallback?: any;
}

/**
*
*/
export interface IMTLReaderInitialValues {
numberOfOutputs?: number;
requestCount?: number;
materials?: object;
interpolateTextures?: boolean;
}

export interface vtkMTLReader extends vtkObject {
/**
*
* @param {String} name
* @param {vtkActor} actor
*/
applyMaterialToActor(name: string, actor: vtkActor): void;

/**
*
*/
getBaseURL(): string;

/**
*
*/
getDataAccessHelper():
| HtmlDataAccessHelper
| HttpDataAccessHelper
| JSZipDataAccessHelper
| LiteHttpDataAccessHelper;

/**
*
*/
getInterpolateTextures(): boolean;

/**
*
* @param {String} name The name of the material.
*/
getMaterial(name: string): object;

/**
*
*/
getMaterialNames(): object;

/**
*
*/
getSplitGroup(): boolean;

/**
* Get the url of the object to load.
*/
getUrl(): string;

/**
*
* @param {Boolean} busy
*/
invokeBusy(busy: boolean): void;

/**
*
*/
isBusy(): number;

/**
*
*/
listImages(): any;

/**
* Load the object data.
* @param {IMTLReaderOptions} [options]
*/
loadData(options?: IMTLReaderOptions): Promise<any>;

/**
*
* @param callback
*/
onBusy(callback: (busy: boolean) => any): vtkSubscription;

/**
* Parse data as text.
* @param {String} content The content to parse.
*/
parseAsText(content: string): void;
/**
*
* @param inData
* @param outData
*/
requestData(inData: any, outData: any): void;

/**
*
* @param dataAccessHelper
*/
setDataAccessHelper(
dataAccessHelper:
| HtmlDataAccessHelper
| HttpDataAccessHelper
| JSZipDataAccessHelper
| LiteHttpDataAccessHelper
): boolean;

/**
*
* @param imagePath
* @param src
*/
setImageSrc(imagePath: string, src: string): any;

/**
*
* @param interpolateTextures
*/
setInterpolateTextures(interpolateTextures: boolean): boolean;

/**
*
* @param splitGroup
*/
setSplitGroup(splitGroup: boolean): boolean;

/**
* Set the url of the object to load.
* @param {String} url the url of the object to load.
* @param {IMTLReaderOptions} [option] The MTL reader options.
*/
setUrl(url: string, option?: IMTLReaderOptions): Promise<string>;
}

/**
* Method used to decorate a given object (publicAPI+model) with vtkMTLReader characteristics.
*
* @param publicAPI object on which methods will be bounds (public)
* @param model object on which data structure will be bounds (protected)
* @param {IMTLReaderInitialValues} [initialValues] (default: {})
*/
export function extend(
publicAPI: object,
model: object,
initialValues?: IMTLReaderInitialValues
): void;

/**
* Method used to create a new instance of vtkMTLReader
* @param {IMTLReaderInitialValues} [initialValues] for pre-setting some of its content
*/
export function newInstance(
initialValues?: IMTLReaderInitialValues
): vtkMTLReader;

/**
* The vtkMTLReader aims to parse the MTL(Material Template Library file format
* which is a companion file format to .OBJ that describes surface shading
* (material) properties of objects within one or more .OBJ files.
*/
export declare const vtkMTLReader: {
newInstance: typeof newInstance;
extend: typeof extend;
};
export default vtkMTLReader;
index.js
import * as macro from 'vtk.js/Sources/macros';
import DataAccessHelper from 'vtk.js/Sources/IO/Core/DataAccessHelper';
import vtkTexture from 'vtk.js/Sources/Rendering/Core/Texture';

// Enable data soure for DataAccessHelper
import 'vtk.js/Sources/IO/Core/DataAccessHelper/LiteHttpDataAccessHelper'; // Just need HTTP
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper'; // HTTP + gz
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HtmlDataAccessHelper'; // html + base64 + zip
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/JSZipDataAccessHelper'; // zip

// ----------------------------------------------------------------------------
// vtkMTLReader methods
// ----------------------------------------------------------------------------

function vtkMTLReader(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkMTLReader');

function imageReady() {
model.requestCount--;
if (model.requestCount === 0) {
publicAPI.invokeBusy(false);
}
}

function parseLine(line) {
if (line[0] === '#' || line.length === 0) {
return;
}

const tokens = line
.split(/[ \t]+/)
.map((s) => s.trim())
.filter((s) => s.length);
if (tokens[0] === 'newmtl') {
tokens.shift();
model.currentMaterial = tokens.join(' ').trim();
} else if (model.currentMaterial) {
if (tokens.length < 2) {
return;
}
if (!model.materials[model.currentMaterial]) {
model.materials[model.currentMaterial] = {};
}
model.materials[model.currentMaterial][tokens[0]] = tokens.slice(1);
if (tokens[0] === 'map_Kd') {
const image = new Image();
image.onload = () => setTimeout(imageReady, 0);
image.src = [model.baseURL, tokens[1]].join('/');
model.materials[model.currentMaterial].image = image;
model.requestCount++;
}
}
}

// Create default dataAccessHelper if not available
if (!model.dataAccessHelper) {
model.dataAccessHelper = DataAccessHelper.get('http');
}

// Internal method to fetch Array
function fetchData(url, options) {
return model.dataAccessHelper.fetchText(publicAPI, url, options);
}

// Set DataSet url
publicAPI.setUrl = (url, option = {}) => {
if (url.indexOf('.mtl') === -1 && !option.fullpath) {
model.baseURL = url;
model.url = `${url}/index.mtl`;
} else {
model.url = url;

// Remove the file in the URL
const path = url.split('/');
path.pop();
model.baseURL = path.join('/');
}

// Fetch metadata
return publicAPI.loadData(option);
};

// Fetch the actual data arrays
publicAPI.loadData = (option) =>
new Promise((resolve, reject) => {
fetchData(model.url, option).then(
(content) => {
publicAPI.parseAsText(content);
resolve();
},
(err) => {
reject();
}
);
});

publicAPI.parseAsText = (content) => {
publicAPI.modified();
model.materials = {};
content.split('\n').forEach(parseLine);
};

// return Busy state
publicAPI.isBusy = () => !!model.requestCount;

publicAPI.getMaterialNames = () => Object.keys(model.materials);
publicAPI.getMaterial = (name) => model.materials[name];

publicAPI.listImages = () =>
Object.keys(model.materials)
.map((name) => model.materials[name].map_Kd)
.filter((fileName) => !!fileName)
.map((s) => s[0].trim());

publicAPI.setImageSrc = (imagePath, src) =>
new Promise((resolve, reject) => {
const selectedName = Object.keys(model.materials).find(
(name) =>
model.materials[name].map_Kd &&
model.materials[name].map_Kd[0].trim() === imagePath.trim()
);
const material = model.materials[selectedName];
if (material && material.image) {
material.image.src = src;
material.image.onload = () => setTimeout(resolve, 0);
} else {
resolve();
}
});

publicAPI.applyMaterialToActor = (name, actor) => {
const material = model.materials[name];
if (material && actor) {
const white = [1, 1, 1];
const actorProp = {
ambientColor: material.Ka ? material.Ka.map((i) => Number(i)) : white,
specularColor: material.Ks ? material.Ks.map((i) => Number(i)) : white,
diffuseColor: material.Kd ? material.Kd.map((i) => Number(i)) : white,
opacity: material.d ? Number(material.d) : 1,
specularPower: material.Ns ? Number(material.Ns) : 1,
};
const illum = Number(material.illum || 2);
['ambient', 'diffuse', 'specular'].forEach((k, idx) => {
actorProp[k] = idx <= illum ? 1.0 : 0.0;
});
if (material.image) {
const texture = vtkTexture.newInstance({
interpolate: model.interpolateTextures,
});
texture.setImage(material.image);
actor.addTexture(texture);
}
actor.getProperty().set(actorProp);
}
};
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
numberOfOutputs: 1,
requestCount: 0,
materials: {},
interpolateTextures: true,
// baseURL: null,
// dataAccessHelper: null,
// url: null,
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);

// Build VTK API
macro.obj(publicAPI, model);
macro.get(publicAPI, model, ['url', 'baseURL']);
macro.setGet(publicAPI, model, [
'dataAccessHelper',
'interpolateTextures',
'splitGroup',
]);
macro.event(publicAPI, model, 'busy');

// Object methods
vtkMTLReader(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(extend, 'vtkMTLReader');

// ----------------------------------------------------------------------------

export default { newInstance, extend };