threepipe
Version:
A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.
198 lines • 9.99 kB
JavaScript
import { ColorManagement, } from 'three';
import { copyProps } from 'ts-browser-helpers';
import { copyMaterialUserData } from '../../utils/serialization';
import { MaterialExtender } from '../../materials';
import { UnlitMaterial } from './UnlitMaterial';
import { threeMaterialInterpolateProps, threeMaterialPropList } from './threeMaterialPropList';
import { lerpParams } from '../../utils/lerp';
export const iMaterialCommons = {
threeMaterialPropList,
threeMaterialInterpolateProps,
setDirty: function (options) {
if (options?.needsUpdate !== false)
this.needsUpdate = true;
this.dispatchEvent({ bubbleToObject: true, bubbleToParent: true, ...options, type: 'materialUpdate' }); // this sets sceneUpdate in root scene
if (options?.last !== false && options?.refreshUi !== false)
this.uiConfig?.uiRefresh?.(true, 'postFrame', 1);
},
setValues: (superSetValues) => function (parameters, _allowInvalidType, clearCurrentUserData, time) {
if (clearCurrentUserData === undefined)
clearCurrentUserData = parameters.isMaterial;
if (clearCurrentUserData)
this.userData = {};
// legacy check for old color management(non-sRGB) in material.setValues todo: move this to Material.fromJSON
const legacyColors = parameters?.metadata && parameters?.metadata.version <= 4.5;
const lastColorManagementEnabled = ColorManagement.enabled;
if (legacyColors)
ColorManagement.enabled = false;
const propList = this.constructor.MaterialProperties;
const params = !propList ? { ...parameters } : copyProps(parameters, {}, Array.from(Object.keys(propList)));
// remove undefined values
for (const key of Object.keys(params))
if (params[key] === undefined)
delete params[key];
const userData = params.userData;
delete params.userData;
const interpolateProps = new Set([...this.constructor.InterpolateProperties || UnlitMaterial.InterpolateProperties, ...this.constructor.MapProperties || UnlitMaterial.MapProperties]);
if (time) {
lerpParams(params, this, interpolateProps, time);
}
// todo: can migrate to @serialize for properties which have UI etc and use super.setValues for the rest like threeMaterialPropList
superSetValues.call(this, params);
if (userData)
copyMaterialUserData(this.userData, userData);
// bump map scale fix todo: move this to Material.fromJSON
// https://github.com/repalash/three.js/commit/7b13bb515866f6a002928bd28d0a793cafeaeb1a
const legacyBumpScale = parameters?.metadata && parameters?.metadata.version <= 4.6;
if ((legacyBumpScale || this.userData.legacyBumpScale) && this?.bumpScale !== undefined && this?.bumpMap && this.defines) {
console.warn('MaterialManager: Old format material loaded, bump map might be incorrect.', parameters, parameters.bumpScale);
this.defines.BUMP_MAP_SCALE_LEGACY = '1';
this.userData.legacyBumpScale = true;
this.needsUpdate = true;
}
if (legacyColors)
ColorManagement.enabled = lastColorManagementEnabled;
this.setDirty && this.setDirty();
return this;
},
dispose: (superDispose) => function (force = true) {
if (!force && this.userData.disposeOnIdle === false)
return;
superDispose.call(this);
},
clone: (superClone) => function (track = false) {
if (track) {
if (!this.userData.cloneId) {
this.userData.cloneId = '0';
}
if (!this.userData.cloneCount) {
this.userData.cloneCount = 0;
}
this.userData.cloneCount += 1;
}
const material = this.generator?.({})?.setValues(this, false) ?? superClone.call(this);
if (track) {
material.userData.cloneId = material.userData.cloneId + '_' + this.userData.cloneCount;
material.userData.cloneCount = 0;
material.name = (material.name || 'mat') + '_' + material.userData.cloneId;
}
return material;
},
dispatchEvent: (superDispatchEvent) => function (event) {
superDispatchEvent.call(this, event);
const type = event.type;
if (event.bubbleToObject && (type === 'beforeDeserialize' || type === 'materialUpdate' || type === 'textureUpdate' || type === 'select' // todo - add more events
)) {
this.appliedMeshes.forEach(m => m.dispatchEvent({ ...event, material: this, type }));
}
},
customProgramCacheKey: function () {
return MaterialExtender.CacheKeyForExtensions(this, this.materialExtensions) + this.userData.inverseAlphaMap;
},
registerMaterialExtensions: function (customMaterialExtensions) {
MaterialExtender.RegisterExtensions(this, customMaterialExtensions);
},
unregisterMaterialExtensions: function (customMaterialExtensions) {
MaterialExtender.UnregisterExtensions(this, customMaterialExtensions);
},
// shader is not Shader but WebglUniforms.getParameters return value type so includes defines
onBeforeCompile: function (shader, renderer) {
if (this.materialExtensions)
MaterialExtender.ApplyMaterialExtensions(this, shader, this.materialExtensions, renderer);
this.dispatchEvent({ type: 'beforeCompile', shader, renderer });
shader.fragmentShader = shader.fragmentShader.replaceAll('#glMarker', '// ');
shader.vertexShader = shader.vertexShader.replaceAll('#glMarker', '// ');
},
onBeforeRender: function (renderer, scene, camera, geometry, object) {
if (this.envMapIntensity !== undefined && !this.userData.separateEnvMapIntensity && scene.envMapIntensity !== undefined) {
this.userData.__envIntensity = this.envMapIntensity;
this.envMapIntensity = scene.envMapIntensity;
}
if (this.defines && this.envMap !== undefined && scene.fixedEnvMapDirection !== undefined) {
if (scene.fixedEnvMapDirection) {
if (!this.defines.FIX_ENV_DIRECTION) {
this.defines.FIX_ENV_DIRECTION = '1';
this.needsUpdate = true;
}
}
else if (this.defines.FIX_ENV_DIRECTION !== undefined) {
delete this.defines.FIX_ENV_DIRECTION;
this.needsUpdate = true;
}
}
this.dispatchEvent({ type: 'beforeRender', renderer, scene, camera, geometry, object });
},
onAfterRender: function (renderer, scene, camera, geometry, object) {
if (this.userData.__envIntensity !== undefined) {
this.envMapIntensity = this.userData.__envIntensity;
delete this.userData.__envIntensity;
}
this.dispatchEvent({ type: 'afterRender', renderer, scene, camera, geometry, object });
},
onBeforeCompileOverride: (superOnBeforeCompile) => function (shader, renderer) {
iMaterialCommons.onBeforeCompile.call(this, shader, renderer);
superOnBeforeCompile.call(this, shader, renderer);
},
onBeforeRenderOverride: (superOnBeforeRender) => function (...args) {
superOnBeforeRender.call(this, ...args);
iMaterialCommons.onBeforeRender.call(this, ...args);
},
onAfterRenderOverride: (superOnAfterRender) => function (...args) {
superOnAfterRender.call(this, ...args);
iMaterialCommons.onAfterRender.call(this, ...args);
},
customProgramCacheKeyOverride: (superCustomPropertyCacheKey) => function () {
return superCustomPropertyCacheKey.call(this) + iMaterialCommons.customProgramCacheKey.call(this);
},
upgradeMaterial: upgradeMaterial,
// todo;
};
/**
* Convert a standard three.js {@link Material} to {@link IMaterial}
*/
export function upgradeMaterial() {
if (!this.isMaterial) {
console.error('Material is not a material', this);
return this;
}
if (!this.setDirty)
this.setDirty = iMaterialCommons.setDirty;
if (!this.appliedMeshes)
this.appliedMeshes = new Set();
if (!this.userData)
this.userData = {};
this.userData.uuid = this.uuid; // for serialization
if (!this.__upgradeSetup) {
this.dispatchEvent = iMaterialCommons.dispatchEvent(this.dispatchEvent);
this.__upgradeSetup = true;
}
// legacy
if (!this.userData.setDirty)
this.userData.setDirty = (e) => {
console.warn('userData.setDirty is deprecated. Use setDirty instead.');
this.setDirty(e);
};
if (this.assetType === 'material')
return this; // already upgraded
this.assetType = 'material';
this.setValues = iMaterialCommons.setValues(this.setValues);
this.dispose = iMaterialCommons.dispose(this.dispose);
this.clone = iMaterialCommons.clone(this.clone);
// material extensions
if (!this.extraUniformsToUpload)
this.extraUniformsToUpload = {};
if (!this.materialExtensions)
this.materialExtensions = [];
if (!this.registerMaterialExtensions)
this.registerMaterialExtensions = iMaterialCommons.registerMaterialExtensions;
if (!this.unregisterMaterialExtensions)
this.unregisterMaterialExtensions = iMaterialCommons.unregisterMaterialExtensions;
this.onBeforeCompile = iMaterialCommons.onBeforeCompileOverride(this.onBeforeCompile);
this.onBeforeRender = iMaterialCommons.onBeforeRenderOverride(this.onBeforeRender);
this.onAfterRender = iMaterialCommons.onAfterRenderOverride(this.onAfterRender);
this.customProgramCacheKey = iMaterialCommons.customProgramCacheKeyOverride(this.customProgramCacheKey);
// todo: add uiconfig, serialization, other stuff from UnlitMaterial?
// dispose uiconfig etc. on dispose
return this;
}
//# sourceMappingURL=iMaterialCommons.js.map