threepipe
Version:
A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.
548 lines • 18.7 kB
JavaScript
import { makeSamplerUi } from '../../ui/image-ui';
import { AdditiveBlending, AlwaysDepth, BackSide, DoubleSide, EqualDepth, FrontSide, GreaterDepth, GreaterEqualDepth, LessDepth, LessEqualDepth, MultiplyBlending, NeverDepth, NoBlending, NormalBlending, NotEqualDepth, ObjectSpaceNormalMap, SubtractiveBlending, TangentSpaceNormalMap, } from 'three';
import { downloadBlob, uploadFile } from 'ts-browser-helpers';
import { getEmptyMeta } from '../../utils';
import { generateUUID } from '../../three/utils';
export const iMaterialUI = {
base: (material) => [
{
type: 'input',
property: [material, 'name'],
},
// {
// type: 'monitor',
// property: [material, 'uuid'],
// },
{
type: 'checkbox',
property: [material, 'wireframe'],
},
{
type: 'checkbox',
property: [material, 'vertexColors'],
},
{
type: 'color',
property: [material, 'color'],
},
material.flatShading !== undefined ? {
type: 'checkbox',
property: [material, 'flatShading'],
} : {},
{
type: 'image',
property: [material, 'map'],
},
makeSamplerUi(material, 'map'),
],
blending: (material) => ({
type: 'folder',
label: 'Blending',
children: [
{
type: 'slider',
bounds: [0, 1],
property: [material, 'opacity'],
},
{
type: 'checkbox',
property: [material, 'transparent'],
onChange: (ev) => material.setDirty({ uiChangeEvent: ev }),
},
{
type: 'dropdown',
property: [material, 'depthFunc'],
children: [
['Never', NeverDepth],
['Always', AlwaysDepth],
['Less', LessDepth],
['LessEqual', LessEqualDepth],
['Equal', EqualDepth],
['GreaterEqual', GreaterEqualDepth],
['Greater', GreaterDepth],
['NotEqual', NotEqualDepth],
].map(value => ({
label: value[0],
value: value[1],
})),
},
{
type: 'checkbox',
property: [material, 'depthTest'],
onChange: (ev) => material.setDirty({ uiChangeEvent: ev }),
},
{
type: 'checkbox',
property: [material, 'depthWrite'],
onChange: (ev) => material.setDirty({ uiChangeEvent: ev }),
},
{
type: 'checkbox',
property: [material, 'colorWrite'],
onChange: (ev) => material.setDirty({ uiChangeEvent: ev }),
},
{
type: 'slider',
bounds: [0, 1],
stepSize: 0.001,
property: [material, 'alphaTest'],
},
{
type: 'checkbox',
property: [material, 'dithering'],
},
{
type: 'dropdown',
label: 'Blending',
property: [material, 'blending'],
children: [
['None', NoBlending],
['Normal', NormalBlending],
['Additive', AdditiveBlending],
['Subtractive', SubtractiveBlending],
['Multiply', MultiplyBlending],
].map(value => ({
label: value[0],
value: value[1],
})),
},
material.alphaMap !== undefined ? {
type: 'image',
property: [material, 'alphaMap'],
} : {},
material.alphaMap !== undefined ? makeSamplerUi(material, 'alphaMap') : {},
{
type: 'checkbox',
label: 'Render to Gbuffer',
// hidden: ()=>!material.transparent && material.transmission < 0.001,
getValue: () => material.userData.renderToGBuffer === true,
setValue: (v) => {
if (!v && !material.userData.renderToGBuffer)
return;
material.userData.renderToGBuffer = v;
material.setDirty();
},
},
{
type: 'checkbox',
label: 'Render to Depth',
hidden: () => material.userData.renderToDepth !== undefined,
getValue: () => material.userData.renderToDepth === true,
setValue: (v) => {
if (!v && !material.userData.renderToDepth)
return;
material.userData.renderToDepth = v;
material.setDirty();
},
},
material.isPhysicalMaterial ? {
type: 'checkbox',
label: 'Inverse AlphaMap',
hidden: () => !material.transparent,
getValue: () => material.userData.inverseAlphaMap === true,
setValue: (v) => {
material.userData.inverseAlphaMap = v ? v : undefined;
material.setDirty();
},
} : {},
],
}),
polygonOffset: (material) => ({
type: 'folder',
label: 'Polygon Offset',
children: [
{
type: 'checkbox',
label: 'Polygon Offset',
property: [material, 'polygonOffset'],
},
{
type: 'slider',
label: 'Polygon Offset Factor',
bounds: [-10, 10],
property: [material, 'polygonOffsetFactor'],
},
{
type: 'slider',
label: 'Polygon Offset Units',
bounds: [-10, 10],
property: [material, 'polygonOffsetUnits'],
},
],
}),
aoLightMap: (material) => ({
type: 'folder',
label: 'AO/Lightmap',
children: [
{
type: 'slider',
bounds: [0, 2],
property: [material, 'aoMapIntensity'],
},
{
type: 'image',
property: [material, 'aoMap'],
},
makeSamplerUi(material, 'aoMap'),
{
type: 'slider',
bounds: [0, 2],
property: [material, 'lightMapIntensity'],
},
{
type: 'image',
property: [material, 'lightMap'],
},
makeSamplerUi(material, 'lightMap'),
],
}),
environment: (material) => ({
type: 'folder',
label: 'Environment',
children: [
{
type: 'checkbox',
label: 'Override Environment',
// property: [this.userData, 'separateEnvMapIntensity'],
getValue: () => material.userData.separateEnvMapIntensity === true,
setValue: (v) => {
material.userData.separateEnvMapIntensity = v;
if (!v)
delete material.userData.separateEnvMapIntensity;
},
// onChange: material.setDirty,
},
{
type: 'slider',
bounds: [0, 20],
hidden: () => !material.userData.separateEnvMapIntensity,
label: 'Environment Intensity',
property: [material, 'envMapIntensity'],
},
{
type: 'dropdown',
hidden: () => !material.userData.separateEnvMapIntensity && !material.userData.envMapSlotKey,
label: 'Environment Map',
children: ['', 'environment1', 'environment2'].map((i) => ({ label: i || 'default', value: i })),
getValue: () => material.userData.envMapSlotKey || '',
setValue: (v) => {
material.userData.envMapSlotKey = v;
if (!v)
delete material.userData.envMapSlotKey;
material.setDirty();
},
},
],
}),
misc: (material) => [
() => material.materialExtensions?.map(v => {
v.uuid = v.uuid || generateUUID();
material.__matExtUiConfigs = material.__matExtUiConfigs || {};
if (!material.__matExtUiConfigs[v.uuid])
material.__matExtUiConfigs[v.uuid] = v.getUiConfig?.(material, material.uiConfig?.uiRefresh);
return material.__matExtUiConfigs[v.uuid];
}).filter(v => v),
{
type: 'dropdown',
label: 'Side',
property: [material, 'side'],
children: [
['Front', FrontSide],
['Back', BackSide],
['Double', DoubleSide],
].map(value => ({
label: value[0],
value: value[1],
})),
},
{
type: 'input',
label: 'Mesh count',
getValue: () => material.appliedMeshes.size || 0,
disabled: true,
},
{
type: 'button',
label: `Download ${material.constructor.TypeSlug}`,
value: () => {
const blob = new Blob([JSON.stringify(material.toJSON(), null, 2)], { type: 'application/json' });
downloadBlob(blob, `material.${material.constructor.TypeSlug}`);
},
},
{
type: 'button',
label: `Select ${material.constructor.TypeSlug}`,
value: async () => uploadFile(false, false, material.constructor.TypeSlug).then(async (files) => files?.[0]?.text()).then((text) => {
if (!text)
return;
const json = JSON.parse(text);
if (json.uuid)
delete json.uuid; // just copy the material properties
const currentJson = material.toJSON();
material.fromJSON(json, getEmptyMeta());
return {
undo: () => material.fromJSON(currentJson, getEmptyMeta()),
redo: () => material.fromJSON(json, getEmptyMeta()),
};
}),
},
],
roughMetal: (material) => ({
type: 'folder',
label: 'Rough/Metal',
children: [
{
type: 'slider',
bounds: [0, 1],
property: [material, 'roughness'],
},
{
type: 'slider',
bounds: [0, 1],
property: [material, 'metalness'],
},
{
type: 'image',
property: [material, 'roughnessMap'],
},
makeSamplerUi(material, 'roughnessMap'),
{
type: 'image',
property: [material, 'metalnessMap'],
},
makeSamplerUi(material, 'metalnessMap'),
],
}),
bumpNormal: (material) => ({
type: 'folder',
// uuid: 'bump_normal',
label: 'Bump/Normal',
children: [
{
type: 'slider',
bounds: [-1, 1],
stepSize: 0.001,
property: [material, 'bumpScale'],
hidden: () => !material.bumpMap,
},
{
type: 'image',
property: [material, 'bumpMap'],
},
makeSamplerUi(material, 'bumpMap'),
{
type: 'image',
property: [material, 'normalMap'],
},
{
type: 'vec2',
property: [material, 'normalScale'],
hidden: () => !material.normalMap,
},
{
type: 'dropdown',
hidden: () => !material.normalMap,
property: [material, 'normalMapType'],
children: [
['TangentSpace', TangentSpaceNormalMap],
['ObjectSpace', ObjectSpaceNormalMap],
].map(value => ({
label: value[0],
value: value[1],
})),
},
makeSamplerUi(material, 'normalMap'),
{
type: 'input',
property: [material, 'displacementScale'],
hidden: () => !material.displacementMap,
},
{
type: 'image',
property: [material, 'displacementMap'],
},
makeSamplerUi(material, 'displacementMap'),
],
}),
emission: (material) => ({
type: 'folder',
label: 'Emission',
children: [
{
type: 'color',
property: [material, 'emissive'],
},
{
type: 'slider',
bounds: [0, 10],
property: [material, 'emissiveIntensity'],
},
{
type: 'image',
property: [material, 'emissiveMap'],
},
makeSamplerUi(material, 'emissiveMap'),
],
}),
transmission: (material) => ({
type: 'folder',
label: 'Refraction',
children: [
// {
// type: 'slider',
// bounds: [0, 1],
// property: [material, 'reflectivity'],
// },
{
type: 'slider',
bounds: [0, 4],
property: [material, 'ior'],
},
{
type: 'slider',
bounds: [0, 1],
property: [material, 'transmission'],
},
{
type: 'slider',
bounds: [0, 1],
stepSize: 0.001,
property: [material, 'thickness'],
},
{
type: 'image',
property: [material, 'transmissionMap'],
},
makeSamplerUi(material, 'transmissionMap'),
{
type: 'image',
property: [material, 'thicknessMap'],
},
makeSamplerUi(material, 'thicknessMap'),
{
type: 'number',
property: [material, 'attenuationDistance'],
},
{
type: 'color',
property: [material, 'attenuationColor'],
},
],
}),
clearcoat: (material) => ({
type: 'folder',
label: 'Clearcoat',
children: [
{
type: 'slider',
bounds: [0, 1],
property: [material, 'clearcoat'],
},
{
type: 'slider',
bounds: [0, 1],
hidden: () => material.clearcoat < 0.001,
property: [material, 'clearcoatRoughness'],
},
{
type: 'image',
property: [material, 'clearcoatMap'],
},
makeSamplerUi(material, 'clearcoatMap'),
{
type: 'slider',
bounds: [0, 1],
property: [material, 'clearcoatRoughness'],
},
{
type: 'image',
property: [material, 'clearcoatRoughnessMap'],
},
makeSamplerUi(material, 'clearcoatRoughnessMap'),
{
type: 'image',
property: [material, 'clearcoatNormalMap'],
},
{
type: 'vec2',
property: [material, 'clearcoatNormalScale'],
hidden: () => !material.clearcoatNormalMap,
},
makeSamplerUi(material, 'clearcoatNormalMap'),
],
}),
iridescence: (material) => ({
type: 'folder',
label: 'Iridescence',
children: [
{
type: 'slider',
bounds: [0, 3],
label: 'Intensity',
property: [material, 'iridescence'],
},
{
type: 'slider',
bounds: [0, 3],
label: 'IOR',
property: [material, 'iridescenceIOR'],
},
{
type: 'slider',
bounds: [0, 500],
label: 'Thickness0',
property: [material.iridescenceThicknessRange, '0'],
onChange: (ev) => material.setDirty({ uiChangeEvent: ev }),
},
{
type: 'slider',
bounds: [0, 500],
label: 'Thickness1',
property: [material.iridescenceThicknessRange, '1'],
onChange: (ev) => material.setDirty({ uiChangeEvent: ev }),
},
{
type: 'image',
property: [material, 'iridescenceMap'],
},
makeSamplerUi(material, 'iridescenceMap'),
{
type: 'image',
property: [material, 'iridescenceThicknessMap'],
},
makeSamplerUi(material, 'iridescenceThicknessMap'),
],
}),
sheen: (material) => ({
type: 'folder',
label: 'Sheen',
children: [
{
type: 'slider',
bounds: [0, 1],
property: [material, 'sheen'],
},
{
type: 'color',
hidden: () => material.sheen < 0.001,
property: [material, 'sheenColor'],
},
{
type: 'image',
property: [material, 'sheenColorMap'],
},
makeSamplerUi(material, 'sheenColorMap'),
{
type: 'slider',
bounds: [0, 1],
property: [material, 'sheenRoughness'],
},
{
type: 'image',
property: [material, 'sheenRoughnessMap'],
},
makeSamplerUi(material, 'sheenRoughnessMap'),
],
}),
};
//# sourceMappingURL=IMaterialUi.js.map