@openhps/core
Version:
Open Hybrid Positioning System - Core component
959 lines (865 loc) • 31.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _Material = require("../Material.js");
var _constants = require("../../constants.js");
var _NodeUtils = require("../../nodes/core/NodeUtils.js");
var _AttributeNode = require("../../nodes/core/AttributeNode.js");
var _PropertyNode = require("../../nodes/core/PropertyNode.js");
var _MaterialNode = require("../../nodes/accessors/MaterialNode.js");
var _ModelViewProjectionNode = require("../../nodes/accessors/ModelViewProjectionNode.js");
var _Normal = require("../../nodes/accessors/Normal.js");
var _InstancedMeshNode = require("../../nodes/accessors/InstancedMeshNode.js");
var _BatchNode = require("../../nodes/accessors/BatchNode.js");
var _MaterialReferenceNode = require("../../nodes/accessors/MaterialReferenceNode.js");
var _Position = require("../../nodes/accessors/Position.js");
var _SkinningNode = require("../../nodes/accessors/SkinningNode.js");
var _MorphNode = require("../../nodes/accessors/MorphNode.js");
var _MathNode = require("../../nodes/math/MathNode.js");
var _TSLBase = require("../../nodes/tsl/TSLBase.js");
var _AONode = _interopRequireDefault(require("../../nodes/lighting/AONode.js"));
var _LightingContextNode = require("../../nodes/lighting/LightingContextNode.js");
var _IrradianceNode = _interopRequireDefault(require("../../nodes/lighting/IrradianceNode.js"));
var _ViewportDepthNode = require("../../nodes/display/ViewportDepthNode.js");
var _Camera = require("../../nodes/accessors/Camera.js");
var _ClippingNode = require("../../nodes/accessors/ClippingNode.js");
var _NodeMaterialObserver = _interopRequireDefault(require("./manager/NodeMaterialObserver.js"));
var _getAlphaHashThreshold = _interopRequireDefault(require("../../nodes/functions/material/getAlphaHashThreshold.js"));
var _ModelNode = require("../../nodes/accessors/ModelNode.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Base class for all node materials.
*
* @augments Material
*/
class NodeMaterial extends _Material.Material {
static get type() {
return 'NodeMaterial';
}
/**
* Represents the type of the node material.
*
* @type {string}
*/
get type() {
return this.constructor.type;
}
set type(_value) {/* */}
/**
* Constructs a new node material.
*/
constructor() {
super();
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isNodeMaterial = true;
/**
* Whether this material is affected by fog or not.
*
* @type {boolean}
* @default true
*/
this.fog = true;
/**
* Whether this material is affected by lights or not.
*
* @type {boolean}
* @default false
*/
this.lights = false;
/**
* Whether this material uses hardware clipping or not.
* This property is managed by the engine and should not be
* modified by apps.
*
* @type {boolean}
* @default false
*/
this.hardwareClipping = false;
/**
* Node materials which set their `lights` property to `true`
* are affected by all lights of the scene. Sometimes selective
* lighting is wanted which means only _some_ lights in the scene
* affect a material. This can be achieved by creating an instance
* of {@link LightsNode} with a list of selective
* lights and assign the node to this property.
*
* ```js
* const customLightsNode = lights( [ light1, light2 ] );
* material.lightsNode = customLightsNode;
* ```
*
* @type {?LightsNode}
* @default null
*/
this.lightsNode = null;
/**
* The environment of node materials can be defined by an environment
* map assigned to the `envMap` property or by `Scene.environment`
* if the node material is a PBR material. This node property allows to overwrite
* the default behavior and define the environment with a custom node.
*
* ```js
* material.envNode = pmremTexture( renderTarget.texture );
* ```
*
* @type {?Node<vec3>}
* @default null
*/
this.envNode = null;
/**
* The lighting of node materials might be influenced by ambient occlusion.
* The default AO is inferred from an ambient occlusion map assigned to `aoMap`
* and the respective `aoMapIntensity`. This node property allows to overwrite
* the default and define the ambient occlusion with a custom node instead.
*
* If you don't want to overwrite the diffuse color but modify the existing
* values instead, use {@link materialAO}.
*
* @type {?Node<float>}
* @default null
*/
this.aoNode = null;
/**
* The diffuse color of node materials is by default inferred from the
* `color` and `map` properties. This node property allows to overwrite the default
* and define the diffuse color with a node instead.
*
* ```js
* material.colorNode = color( 0xff0000 ); // define red color
* ```
*
* If you don't want to overwrite the diffuse color but modify the existing
* values instead, use {@link materialColor}.
*
* ```js
* material.colorNode = materialColor.mul( color( 0xff0000 ) ); // give diffuse colors a red tint
* ```
*
* @type {?Node<vec3>}
* @default null
*/
this.colorNode = null;
/**
* The normals of node materials are by default inferred from the `normalMap`/`normalScale`
* or `bumpMap`/`bumpScale` properties. This node property allows to overwrite the default
* and define the normals with a node instead.
*
* If you don't want to overwrite the normals but modify the existing values instead,
* use {@link materialNormal}.
*
* @type {?Node<vec3>}
* @default null
*/
this.normalNode = null;
/**
* The opacity of node materials is by default inferred from the `opacity`
* and `alphaMap` properties. This node property allows to overwrite the default
* and define the opacity with a node instead.
*
* If you don't want to overwrite the normals but modify the existing
* value instead, use {@link materialOpacity}.
*
* @type {?Node<float>}
* @default null
*/
this.opacityNode = null;
/**
* This node can be used to to implement a variety of filter-like effects. The idea is
* to store the current rendering into a texture e.g. via `viewportSharedTexture()`, use it
* to create an arbitrary effect and then assign the node composition to this property.
* Everything behind the object using this material will now be affected by a filter.
*
* ```js
* const material = new NodeMaterial()
* material.transparent = true;
*
* // everything behind the object will be monochromatic
* material.backdropNode = saturation( viewportSharedTexture().rgb, 0 );
* ```
*
* Backdrop computations are part of the lighting so only lit materials can use this property.
*
* @type {?Node<vec3>}
* @default null
*/
this.backdropNode = null;
/**
* This node allows to modulate the influence of `backdropNode` to the outgoing light.
*
* @type {?Node<float>}
* @default null
*/
this.backdropAlphaNode = null;
/**
* The alpha test of node materials is by default inferred from the `alphaTest`
* property. This node property allows to overwrite the default and define the
* alpha test with a node instead.
*
* If you don't want to overwrite the alpha test but modify the existing
* value instead, use {@link materialAlphaTest}.
*
* @type {?Node<float>}
* @default null
*/
this.alphaTestNode = null;
/**
* The local vertex positions are computed based on multiple factors like the
* attribute data, morphing or skinning. This node property allows to overwrite
* the default and define local vertex positions with nodes instead.
*
* If you don't want to overwrite the vertex positions but modify the existing
* values instead, use {@link positionLocal}.
*
*```js
* material.positionNode = positionLocal.add( displace );
* ```
*
* @type {?Node<vec3>}
* @default null
*/
this.positionNode = null;
/**
* This node property is intended for logic which modifies geometry data once or per animation step.
* Apps usually place such logic randomly in initialization routines or in the animation loop.
* `geometryNode` is intended as a dedicated API so there is an intended spot where geometry modifications
* can be implemented.
*
* The idea is to assign a `Fn` definition that holds the geometry modification logic. A typical example
* would be a GPU based particle system that provides a node material for usage on app level. The particle
* simulation would be implemented as compute shaders and managed inside a `Fn` function. This function is
* eventually assigned to `geometryNode`.
*
* @type {?Function}
* @default null
*/
this.geometryNode = null;
/**
* Allows to overwrite depth values in the fragment shader.
*
* @type {?Node<float>}
* @default null
*/
this.depthNode = null;
/**
* Allows to overwrite the position used for shadow map rendering which
* is by default {@link positionWorld}, the vertex position
* in world space.
*
* @type {?Node<float>}
* @default null
*/
this.shadowPositionNode = null;
/**
* This node can be used to influence how an object using this node material
* receive shadows.
*
* ```js
* const totalShadows = float( 1 ).toVar();
* material.receivedShadowNode = Fn( ( [ shadow ] ) => {
* totalShadows.mulAssign( shadow );
* //return float( 1 ); // bypass received shadows
* return shadow.mix( color( 0xff0000 ), 1 ); // modify shadow color
* } );
*
* @type {?(Function|FunctionNode<vec4>)}
* @default null
*/
this.receivedShadowNode = null;
/**
* This node can be used to influence how an object using this node material
* casts shadows. To apply a color to shadows, you can simply do:
*
* ```js
* material.castShadowNode = vec4( 1, 0, 0, 1 );
* ```
*
* Which can be nice to fake colored shadows of semi-transparent objects. It
* is also common to use the property with `Fn` function so checks are performed
* per fragment.
*
* ```js
* materialCustomShadow.castShadowNode = Fn( () => {
* hash( vertexIndex ).greaterThan( 0.5 ).discard();
* return materialColor;
* } )();
* ```
*
* @type {?Node<vec4>}
* @default null
*/
this.castShadowNode = null;
/**
* This node can be used to define the final output of the material.
*
* TODO: Explain the differences to `fragmentNode`.
*
* @type {?Node<vec4>}
* @default null
*/
this.outputNode = null;
/**
* MRT configuration is done on renderer or pass level. This node allows to
* overwrite what values are written into MRT targets on material level. This
* can be useful for implementing selective FX features that should only affect
* specific objects.
*
* @type {?MRTNode}
* @default null
*/
this.mrtNode = null;
/**
* This node property can be used if you need complete freedom in implementing
* the fragment shader. Assigning a node will replace the built-in material
* logic used in the fragment stage.
*
* @type {?Node<vec4>}
* @default null
*/
this.fragmentNode = null;
/**
* This node property can be used if you need complete freedom in implementing
* the vertex shader. Assigning a node will replace the built-in material logic
* used in the vertex stage.
*
* @type {?Node<vec4>}
* @default null
*/
this.vertexNode = null;
}
/**
* Allows to define a custom cache key that influence the material key computation
* for render objects.
*
* @return {string} The custom cache key.
*/
customProgramCacheKey() {
return this.type + (0, _NodeUtils.getCacheKey)(this);
}
/**
* Builds this material with the given node builder.
*
* @param {NodeBuilder} builder - The current node builder.
*/
build(builder) {
this.setup(builder);
}
/**
* Setups a node material observer with the given builder.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {NodeMaterialObserver} The node material observer.
*/
setupObserver(builder) {
return new _NodeMaterialObserver.default(builder);
}
/**
* Setups the vertex and fragment stage of this node material.
*
* @param {NodeBuilder} builder - The current node builder.
*/
setup(builder) {
builder.context.setupNormal = () => this.setupNormal(builder);
builder.context.setupPositionView = () => this.setupPositionView(builder);
builder.context.setupModelViewProjection = () => this.setupModelViewProjection(builder);
const renderer = builder.renderer;
const renderTarget = renderer.getRenderTarget();
// < VERTEX STAGE >
builder.addStack();
const vertexNode = this.vertexNode || this.setupVertex(builder);
builder.stack.outputNode = vertexNode;
this.setupHardwareClipping(builder);
if (this.geometryNode !== null) {
builder.stack.outputNode = builder.stack.outputNode.bypass(this.geometryNode);
}
builder.addFlow('vertex', builder.removeStack());
// < FRAGMENT STAGE >
builder.addStack();
let resultNode;
const clippingNode = this.setupClipping(builder);
if (this.depthWrite === true || this.depthTest === true) {
// only write depth if depth buffer is configured
if (renderTarget !== null) {
if (renderTarget.depthBuffer === true) this.setupDepth(builder);
} else {
if (renderer.depth === true) this.setupDepth(builder);
}
}
if (this.fragmentNode === null) {
this.setupDiffuseColor(builder);
this.setupVariants(builder);
const outgoingLightNode = this.setupLighting(builder);
if (clippingNode !== null) builder.stack.add(clippingNode);
// force unsigned floats - useful for RenderTargets
const basicOutput = (0, _TSLBase.vec4)(outgoingLightNode, _PropertyNode.diffuseColor.a).max(0);
resultNode = this.setupOutput(builder, basicOutput);
// OUTPUT NODE
_PropertyNode.output.assign(resultNode);
//
const isCustomOutput = this.outputNode !== null;
if (isCustomOutput) resultNode = this.outputNode;
// MRT
if (renderTarget !== null) {
const mrt = renderer.getMRT();
const materialMRT = this.mrtNode;
if (mrt !== null) {
if (isCustomOutput) _PropertyNode.output.assign(resultNode);
resultNode = mrt;
if (materialMRT !== null) {
resultNode = mrt.merge(materialMRT);
}
} else if (materialMRT !== null) {
resultNode = materialMRT;
}
}
} else {
let fragmentNode = this.fragmentNode;
if (fragmentNode.isOutputStructNode !== true) {
fragmentNode = (0, _TSLBase.vec4)(fragmentNode);
}
resultNode = this.setupOutput(builder, fragmentNode);
}
builder.stack.outputNode = resultNode;
builder.addFlow('fragment', builder.removeStack());
// < OBSERVER >
builder.observer = this.setupObserver(builder);
}
/**
* Setups the clipping node.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {ClippingNode} The clipping node.
*/
setupClipping(builder) {
if (builder.clippingContext === null) return null;
const {
unionPlanes,
intersectionPlanes
} = builder.clippingContext;
let result = null;
if (unionPlanes.length > 0 || intersectionPlanes.length > 0) {
const samples = builder.renderer.samples;
if (this.alphaToCoverage && samples > 1) {
// to be added to flow when the color/alpha value has been determined
result = (0, _ClippingNode.clippingAlpha)();
} else {
builder.stack.add((0, _ClippingNode.clipping)());
}
}
return result;
}
/**
* Setups the hardware clipping if available on the current device.
*
* @param {NodeBuilder} builder - The current node builder.
*/
setupHardwareClipping(builder) {
this.hardwareClipping = false;
if (builder.clippingContext === null) return;
const candidateCount = builder.clippingContext.unionPlanes.length;
// 8 planes supported by WebGL ANGLE_clip_cull_distance and WebGPU clip-distances
if (candidateCount > 0 && candidateCount <= 8 && builder.isAvailable('clipDistance')) {
builder.stack.add((0, _ClippingNode.hardwareClipping)());
this.hardwareClipping = true;
}
return;
}
/**
* Setups the depth of this material.
*
* @param {NodeBuilder} builder - The current node builder.
*/
setupDepth(builder) {
const {
renderer,
camera
} = builder;
// Depth
let depthNode = this.depthNode;
if (depthNode === null) {
const mrt = renderer.getMRT();
if (mrt && mrt.has('depth')) {
depthNode = mrt.get('depth');
} else if (renderer.logarithmicDepthBuffer === true) {
if (camera.isPerspectiveCamera) {
depthNode = (0, _ViewportDepthNode.viewZToLogarithmicDepth)(_Position.positionView.z, _Camera.cameraNear, _Camera.cameraFar);
} else {
depthNode = (0, _ViewportDepthNode.viewZToOrthographicDepth)(_Position.positionView.z, _Camera.cameraNear, _Camera.cameraFar);
}
}
}
if (depthNode !== null) {
_ViewportDepthNode.depth.assign(depthNode).append();
}
}
/**
* Setups the position node in view space. This method exists
* so derived node materials can modify the implementation e.g. sprite materials.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node<vec3>} The position in view space.
*/
setupPositionView( /*builder*/
) {
return _ModelNode.modelViewMatrix.mul(_Position.positionLocal).xyz;
}
/**
* Setups the position in clip space.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node<vec4>} The position in view space.
*/
setupModelViewProjection( /*builder*/
) {
return _Camera.cameraProjectionMatrix.mul(_Position.positionView);
}
/**
* Setups the logic for the vertex stage.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node<vec4>} The position in clip space.
*/
setupVertex(builder) {
builder.addStack();
this.setupPosition(builder);
builder.context.vertex = builder.removeStack();
return _ModelViewProjectionNode.modelViewProjection;
}
/**
* Setups the computation of the position in local space.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node<vec3>} The position in local space.
*/
setupPosition(builder) {
const {
object,
geometry
} = builder;
if (geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color) {
(0, _MorphNode.morphReference)(object).append();
}
if (object.isSkinnedMesh === true) {
(0, _SkinningNode.skinning)(object).append();
}
if (this.displacementMap) {
const displacementMap = (0, _MaterialReferenceNode.materialReference)('displacementMap', 'texture');
const displacementScale = (0, _MaterialReferenceNode.materialReference)('displacementScale', 'float');
const displacementBias = (0, _MaterialReferenceNode.materialReference)('displacementBias', 'float');
_Position.positionLocal.addAssign(_Normal.normalLocal.normalize().mul(displacementMap.x.mul(displacementScale).add(displacementBias)));
}
if (object.isBatchedMesh) {
(0, _BatchNode.batch)(object).append();
}
if (object.isInstancedMesh && object.instanceMatrix && object.instanceMatrix.isInstancedBufferAttribute === true) {
(0, _InstancedMeshNode.instancedMesh)(object).append();
}
if (this.positionNode !== null) {
_Position.positionLocal.assign(this.positionNode.context({
isPositionNodeInput: true
}));
}
return _Position.positionLocal;
}
/**
* Setups the computation of the material's diffuse color.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {BufferGeometry} geometry - The geometry.
*/
setupDiffuseColor({
object,
geometry
}) {
let colorNode = this.colorNode ? (0, _TSLBase.vec4)(this.colorNode) : _MaterialNode.materialColor;
// VERTEX COLORS
if (this.vertexColors === true && geometry.hasAttribute('color')) {
colorNode = (0, _TSLBase.vec4)(colorNode.xyz.mul((0, _AttributeNode.attribute)('color', 'vec3')), colorNode.a);
}
// Instanced colors
if (object.instanceColor) {
const instanceColor = (0, _PropertyNode.varyingProperty)('vec3', 'vInstanceColor');
colorNode = instanceColor.mul(colorNode);
}
if (object.isBatchedMesh && object._colorsTexture) {
const batchColor = (0, _PropertyNode.varyingProperty)('vec3', 'vBatchColor');
colorNode = batchColor.mul(colorNode);
}
// COLOR
_PropertyNode.diffuseColor.assign(colorNode);
// OPACITY
const opacityNode = this.opacityNode ? (0, _TSLBase.float)(this.opacityNode) : _MaterialNode.materialOpacity;
_PropertyNode.diffuseColor.a.assign(_PropertyNode.diffuseColor.a.mul(opacityNode));
// ALPHA TEST
if (this.alphaTestNode !== null || this.alphaTest > 0) {
const alphaTestNode = this.alphaTestNode !== null ? (0, _TSLBase.float)(this.alphaTestNode) : _MaterialNode.materialAlphaTest;
_PropertyNode.diffuseColor.a.lessThanEqual(alphaTestNode).discard();
}
// ALPHA HASH
if (this.alphaHash === true) {
_PropertyNode.diffuseColor.a.lessThan((0, _getAlphaHashThreshold.default)(_Position.positionLocal)).discard();
}
if (this.transparent === false && this.blending === _constants.NormalBlending && this.alphaToCoverage === false) {
_PropertyNode.diffuseColor.a.assign(1.0);
}
}
/**
* Abstract interface method that can be implemented by derived materials
* to setup material-specific node variables.
*
* @abstract
* @param {NodeBuilder} builder - The current node builder.
*/
setupVariants( /*builder*/
) {
// Interface function.
}
/**
* Setups the outgoing light node variable
*
* @return {Node<vec3>} The outgoing light node.
*/
setupOutgoingLight() {
return this.lights === true ? (0, _TSLBase.vec3)(0) : _PropertyNode.diffuseColor.rgb;
}
/**
* Setups the normal node from the material.
*
* @return {Node<vec3>} The normal node.
*/
setupNormal() {
return this.normalNode ? (0, _TSLBase.vec3)(this.normalNode) : _MaterialNode.materialNormal;
}
/**
* Setups the environment node from the material.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node<vec4>} The environment node.
*/
setupEnvironment( /*builder*/
) {
let node = null;
if (this.envNode) {
node = this.envNode;
} else if (this.envMap) {
node = this.envMap.isCubeTexture ? (0, _MaterialReferenceNode.materialReference)('envMap', 'cubeTexture') : (0, _MaterialReferenceNode.materialReference)('envMap', 'texture');
}
return node;
}
/**
* Setups the light map node from the material.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node<vec3>} The light map node.
*/
setupLightMap(builder) {
let node = null;
if (builder.material.lightMap) {
node = new _IrradianceNode.default(_MaterialNode.materialLightMap);
}
return node;
}
/**
* Setups the lights node based on the scene, environment and material.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {LightsNode} The lights node.
*/
setupLights(builder) {
const materialLightsNode = [];
//
const envNode = this.setupEnvironment(builder);
if (envNode && envNode.isLightingNode) {
materialLightsNode.push(envNode);
}
const lightMapNode = this.setupLightMap(builder);
if (lightMapNode && lightMapNode.isLightingNode) {
materialLightsNode.push(lightMapNode);
}
if (this.aoNode !== null || builder.material.aoMap) {
const aoNode = this.aoNode !== null ? this.aoNode : _MaterialNode.materialAO;
materialLightsNode.push(new _AONode.default(aoNode));
}
let lightsN = this.lightsNode || builder.lightsNode;
if (materialLightsNode.length > 0) {
lightsN = builder.renderer.lighting.createNode([...lightsN.getLights(), ...materialLightsNode]);
}
return lightsN;
}
/**
* This method should be implemented by most derived materials
* since it defines the material's lighting model.
*
* @abstract
* @param {NodeBuilder} builder - The current node builder.
* @return {LightingModel} The lighting model.
*/
setupLightingModel( /*builder*/
) {
// Interface function.
}
/**
* Setups the outgoing light node.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Node<vec3>} The outgoing light node.
*/
setupLighting(builder) {
const {
material
} = builder;
const {
backdropNode,
backdropAlphaNode,
emissiveNode
} = this;
// OUTGOING LIGHT
const lights = this.lights === true || this.lightsNode !== null;
const lightsNode = lights ? this.setupLights(builder) : null;
let outgoingLightNode = this.setupOutgoingLight(builder);
if (lightsNode && lightsNode.getScope().hasLights) {
const lightingModel = this.setupLightingModel(builder) || null;
outgoingLightNode = (0, _LightingContextNode.lightingContext)(lightsNode, lightingModel, backdropNode, backdropAlphaNode);
} else if (backdropNode !== null) {
outgoingLightNode = (0, _TSLBase.vec3)(backdropAlphaNode !== null ? (0, _MathNode.mix)(outgoingLightNode, backdropNode, backdropAlphaNode) : backdropNode);
}
// EMISSIVE
if (emissiveNode && emissiveNode.isNode === true || material.emissive && material.emissive.isColor === true) {
_PropertyNode.emissive.assign((0, _TSLBase.vec3)(emissiveNode ? emissiveNode : _MaterialNode.materialEmissive));
outgoingLightNode = outgoingLightNode.add(_PropertyNode.emissive);
}
return outgoingLightNode;
}
/**
* Setup the fog.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {Node<vec4>} outputNode - The existing output node.
* @return {Node<vec4>} The output node.
*/
setupFog(builder, outputNode) {
const fogNode = builder.fogNode;
if (fogNode) {
_PropertyNode.output.assign(outputNode);
outputNode = (0, _TSLBase.vec4)(fogNode);
}
return outputNode;
}
/**
* Setups the output node.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {Node<vec4>} outputNode - The existing output node.
* @return {Node<vec4>} The output node.
*/
setupOutput(builder, outputNode) {
// FOG
if (this.fog === true) {
outputNode = this.setupFog(builder, outputNode);
}
return outputNode;
}
/**
* Most classic material types have a node pendant e.g. for `MeshBasicMaterial`
* there is `MeshBasicNodeMaterial`. This utility method is intended for
* defining all material properties of the classic type in the node type.
*
* @param {Material} material - The material to copy properties with their values to this node material.
*/
setDefaultValues(material) {
// This approach is to reuse the native refreshUniforms*
// and turn available the use of features like transmission and environment in core
for (const property in material) {
const value = material[property];
if (this[property] === undefined) {
this[property] = value;
if (value && value.clone) this[property] = value.clone();
}
}
const descriptors = Object.getOwnPropertyDescriptors(material.constructor.prototype);
for (const key in descriptors) {
if (Object.getOwnPropertyDescriptor(this.constructor.prototype, key) === undefined && descriptors[key].get !== undefined) {
Object.defineProperty(this.constructor.prototype, key, descriptors[key]);
}
}
}
/**
* Serializes this material to JSON.
*
* @param {?(Object|string)} meta - The meta information for serialization.
* @return {Object} The serialized node.
*/
toJSON(meta) {
const isRoot = meta === undefined || typeof meta === 'string';
if (isRoot) {
meta = {
textures: {},
images: {},
nodes: {}
};
}
const data = _Material.Material.prototype.toJSON.call(this, meta);
const nodeChildren = (0, _NodeUtils.getNodeChildren)(this);
data.inputNodes = {};
for (const {
property,
childNode
} of nodeChildren) {
data.inputNodes[property] = childNode.toJSON(meta).uuid;
}
// TODO: Copied from Object3D.toJSON
function extractFromCache(cache) {
const values = [];
for (const key in cache) {
const data = cache[key];
delete data.metadata;
values.push(data);
}
return values;
}
if (isRoot) {
const textures = extractFromCache(meta.textures);
const images = extractFromCache(meta.images);
const nodes = extractFromCache(meta.nodes);
if (textures.length > 0) data.textures = textures;
if (images.length > 0) data.images = images;
if (nodes.length > 0) data.nodes = nodes;
}
return data;
}
/**
* Copies the properties of the given node material to this instance.
*
* @param {NodeMaterial} source - The material to copy.
* @return {NodeMaterial} A reference to this node material.
*/
copy(source) {
this.lightsNode = source.lightsNode;
this.envNode = source.envNode;
this.colorNode = source.colorNode;
this.normalNode = source.normalNode;
this.opacityNode = source.opacityNode;
this.backdropNode = source.backdropNode;
this.backdropAlphaNode = source.backdropAlphaNode;
this.alphaTestNode = source.alphaTestNode;
this.positionNode = source.positionNode;
this.geometryNode = source.geometryNode;
this.depthNode = source.depthNode;
this.shadowPositionNode = source.shadowPositionNode;
this.receivedShadowNode = source.receivedShadowNode;
this.castShadowNode = source.castShadowNode;
this.outputNode = source.outputNode;
this.mrtNode = source.mrtNode;
this.fragmentNode = source.fragmentNode;
this.vertexNode = source.vertexNode;
return super.copy(source);
}
}
var _default = exports.default = NodeMaterial;