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.
207 lines • 9.23 kB
JavaScript
import { Color, LineBasicMaterial, } from 'three';
import { shaderReplaceString, ThreeSerialization } from '../../utils';
import { iMaterialCommons } from './iMaterialCommons';
import { makeSamplerUi } from '../../ui/image-ui';
import { iMaterialUI } from './IMaterialUi';
import { threeMaterialInterpolateProps, threeMaterialPropList } from './threeMaterialPropList';
/**
* And extension of three.js LineBasicMaterial that can be assigned to lines, and support threepipe features, uiconfig, and serialization.
*
* @category Materials
*/
export class UnlitLineMaterial extends LineBasicMaterial {
dispose() { return iMaterialCommons.dispose(super.dispose).call(this); }
clone(track = false) { return iMaterialCommons.clone(super.clone).call(this, track); }
constructor({ customMaterialExtensions, ...parameters } = {}) {
super();
this.assetType = 'material';
this.isUnlitLineMaterial = true;
this.appliedMeshes = new Set();
this.setDirty = iMaterialCommons.setDirty;
// region Material Extension
this.materialExtensions = [];
this.extraUniformsToUpload = {};
this.registerMaterialExtensions = iMaterialCommons.registerMaterialExtensions;
this.unregisterMaterialExtensions = iMaterialCommons.unregisterMaterialExtensions;
this.onBeforeRender = iMaterialCommons.onBeforeRenderOverride(super.onBeforeRender);
this.onAfterRender = iMaterialCommons.onAfterRenderOverride(super.onAfterRender);
// endregion
// region UI Config
// todo dispose ui config
this.uiConfig = {
type: 'folder',
label: 'Unlit Line Material',
uuid: 'MBLM2_' + this.uuid,
expanded: true,
onChange: (ev) => {
if (!ev.config || ev.config.onChange)
return;
let key = Array.isArray(ev.config.property) ? ev.config.property[1] : ev.config.property;
key = typeof key === 'string' ? key : undefined;
// todo set needsUpdate true only for properties that require it like maps.
this.setDirty({ uiChangeEvent: ev, needsUpdate: !!ev.last, refreshUi: !!ev.last, change: key });
},
children: [
{
type: 'input',
property: [this, 'name'],
},
// {
// type: 'monitor',
// property: [this, 'uuid'],
// },
{
type: 'checkbox',
property: [this, 'vertexColors'],
},
{
type: 'color',
property: [this, 'color'],
},
makeSamplerUi(this, 'map'),
{
type: 'number',
property: [this, 'linewidth'],
},
{
type: 'dropdown',
property: [this, 'linecap'],
children: ['butt', 'round', 'square'].map(label => ({ label })),
},
{
type: 'dropdown',
property: [this, 'linejoin'],
children: ['bevel', 'round', 'miter'].map(label => ({ label })),
},
// {
// type: 'checkbox',
// property: [this, 'fog'],
// },
iMaterialUI.blending(this),
iMaterialUI.polygonOffset(this),
...iMaterialUI.misc(this),
],
};
!this.defines && (this.defines = {});
this.fog = false;
this.setDirty = this.setDirty.bind(this);
if (customMaterialExtensions)
this.registerMaterialExtensions(customMaterialExtensions);
iMaterialCommons.upgradeMaterial.call(this);
this.setValues(parameters);
}
customProgramCacheKey() {
return super.customProgramCacheKey() + iMaterialCommons.customProgramCacheKey.call(this);
}
onBeforeCompile(shader, renderer) {
const f = [
['vec3 outgoingLight = ', 'afterModulation'], // added markers before found substring
['#include <aomap_fragment>', 'beforeModulation'],
['ReflectedLight reflectedLight = ', 'beforeAccumulation'],
['#include <clipping_planes_fragment>', 'mainStart'],
];
const v = [
['#include <uv_vertex>', 'mainStart'],
];
for (const vElement of v)
shader.vertexShader = shaderReplaceString(shader.vertexShader, vElement[0], '#glMarker ' + vElement[1] + '\n' + vElement[0]);
for (const vElement of f)
shader.fragmentShader = shaderReplaceString(shader.fragmentShader, vElement[0], '#glMarker ' + vElement[1] + '\n' + vElement[0]);
iMaterialCommons.onBeforeCompile.call(this, shader, renderer);
super.onBeforeCompile(shader, renderer);
}
// endregion
// region Serialization
/**
* Sets the values of this material based on the values of the passed material or an object with material properties
* The input is expected to be a valid material or a deserialized material parameters object(including the deserialized userdata)
* @param parameters - material or material parameters object
* @param allowInvalidType - if true, the type of the oldMaterial is not checked. Objects without type are always allowed.
* @param clearCurrentUserData - if undefined, then depends on material.isMaterial. if true, the current userdata is cleared before setting the new values, because it can have data which won't be overwritten if not present in the new material.
* @param time - optional data to animate(lerp) from current value to the target value.
*/
setValues(parameters, allowInvalidType = true, clearCurrentUserData = undefined, time) {
if (!parameters)
return this;
if (parameters.type && !allowInvalidType && !['LineBasicMaterial', 'LineBasicMaterial2', this.constructor.TYPE].includes(parameters.type)) {
console.error('Material type is not supported:', parameters.type);
return this;
}
iMaterialCommons.setValues(super.setValues).call(this, parameters, allowInvalidType, clearCurrentUserData, time);
this.userData.uuid = this.uuid;
return this;
}
copy(source) {
return this.setValues(source, false);
}
/**
* Serializes this material to JSON.
* @param meta - metadata for serialization
* @param _internal - Calls only super.toJSON, does internal three.js serialization and `@serialize` tags. Set it to true only if you know what you are doing. This is used in Serialization->serializer->material
*/
toJSON(meta, _internal = false) {
if (_internal)
return {
...super.toJSON(meta),
...ThreeSerialization.Serialize(this, meta, true), // this will serialize the properties of this class(like defined with @serialize and @serialize attribute)
};
return ThreeSerialization.Serialize(this, meta, false); // this will call toJSON again, but with _internal=true, that's why we set isThis to false.
}
/**
* Deserializes the material from JSON.
* Textures should be loaded and in meta.textures before calling this method.
* todo - needs to be tested
* @param data
* @param meta
* @param _internal
*/
fromJSON(data, meta, _internal = false) {
if (_internal) {
ThreeSerialization.Deserialize(data, this, meta, true);
return this.setValues(data);
}
this.dispatchEvent({ type: 'beforeDeserialize', data, meta, bubbleToObject: true, bubbleToParent: true });
return this;
}
}
UnlitLineMaterial.TypeSlug = 'blmat';
UnlitLineMaterial.TYPE = 'UnlitLineMaterial'; // not using .type because it is used by three.js
// endregion UI Config
// Class properties can also be listed with annotations like @serialize or @property
// used for serialization
UnlitLineMaterial.MaterialProperties = {
...threeMaterialPropList,
color: new Color(0xffffff),
map: null,
linewidth: 1,
linecap: 'round',
linejoin: 'round',
fog: true,
};
UnlitLineMaterial.MapProperties = [
'map',
];
UnlitLineMaterial.InterpolateProperties = [
...threeMaterialInterpolateProps,
'color',
'linewidth',
];
UnlitLineMaterial.MaterialTemplate = {
materialType: UnlitLineMaterial.TYPE,
name: 'unlit_line',
typeSlug: UnlitLineMaterial.TypeSlug,
alias: ['line_basic', 'unlit_line', UnlitLineMaterial.TYPE, UnlitLineMaterial.TypeSlug, 'LineBasicMaterial', 'LineBasicMaterial2'],
params: {
color: new Color(1, 1, 1),
},
generator: (params) => {
return new UnlitLineMaterial(params);
},
};
export class LineBasicMaterial2 extends UnlitLineMaterial {
constructor(parameters) {
super(parameters);
console.error('LineBasicMaterial2 is deprecated, use UnlitLineMaterial instead');
}
}
//# sourceMappingURL=UnlitLineMaterial.js.map