UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

201 lines (192 loc) 7.48 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.hardwareClipping = exports.default = exports.clippingAlpha = exports.clipping = void 0; var _Node = _interopRequireDefault(require("../core/Node.js")); var _TSLBase = require("../tsl/TSLBase.js"); var _Position = require("./Position.js"); var _PropertyNode = require("../core/PropertyNode.js"); var _LoopNode = require("../utils/LoopNode.js"); var _MathNode = require("../math/MathNode.js"); var _UniformArrayNode = require("./UniformArrayNode.js"); var _BuiltinNode = require("./BuiltinNode.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * This node is used in {@link NodeMaterial} to setup the clipping * which can happen hardware-accelerated (if supported) and optionally * use alpha-to-coverage for anti-aliasing clipped edges. * * @augments Node */ class ClippingNode extends _Node.default { static get type() { return 'ClippingNode'; } /** * Constructs a new clipping node. * * @param {('default'|'hardware'|'alphaToCoverage')} [scope='default'] - The node's scope. Similar to other nodes, * the selected scope influences the behavior of the node and what type of code is generated. */ constructor(scope = ClippingNode.DEFAULT) { super(); /** * The node's scope. Similar to other nodes, the selected scope influences * the behavior of the node and what type of code is generated. * * @type {('default'|'hardware'|'alphaToCoverage')} */ this.scope = scope; } /** * Setups the node depending on the selected scope. * * @param {NodeBuilder} builder - The current node builder. * @return {Node} The result node. */ setup(builder) { super.setup(builder); const clippingContext = builder.clippingContext; const { intersectionPlanes, unionPlanes } = clippingContext; this.hardwareClipping = builder.material.hardwareClipping; if (this.scope === ClippingNode.ALPHA_TO_COVERAGE) { return this.setupAlphaToCoverage(intersectionPlanes, unionPlanes); } else if (this.scope === ClippingNode.HARDWARE) { return this.setupHardwareClipping(unionPlanes, builder); } else { return this.setupDefault(intersectionPlanes, unionPlanes); } } /** * Setups alpha to coverage. * * @param {Array<Vector4>} intersectionPlanes - The intersection planes. * @param {Array<Vector4>} unionPlanes - The union planes. * @return {Node} The result node. */ setupAlphaToCoverage(intersectionPlanes, unionPlanes) { return (0, _TSLBase.Fn)(() => { const distanceToPlane = (0, _TSLBase.float)().toVar('distanceToPlane'); const distanceGradient = (0, _TSLBase.float)().toVar('distanceToGradient'); const clipOpacity = (0, _TSLBase.float)(1).toVar('clipOpacity'); const numUnionPlanes = unionPlanes.length; if (this.hardwareClipping === false && numUnionPlanes > 0) { const clippingPlanes = (0, _UniformArrayNode.uniformArray)(unionPlanes); (0, _LoopNode.Loop)(numUnionPlanes, ({ i }) => { const plane = clippingPlanes.element(i); distanceToPlane.assign(_Position.positionView.dot(plane.xyz).negate().add(plane.w)); distanceGradient.assign(distanceToPlane.fwidth().div(2.0)); clipOpacity.mulAssign((0, _MathNode.smoothstep)(distanceGradient.negate(), distanceGradient, distanceToPlane)); }); } const numIntersectionPlanes = intersectionPlanes.length; if (numIntersectionPlanes > 0) { const clippingPlanes = (0, _UniformArrayNode.uniformArray)(intersectionPlanes); const intersectionClipOpacity = (0, _TSLBase.float)(1).toVar('intersectionClipOpacity'); (0, _LoopNode.Loop)(numIntersectionPlanes, ({ i }) => { const plane = clippingPlanes.element(i); distanceToPlane.assign(_Position.positionView.dot(plane.xyz).negate().add(plane.w)); distanceGradient.assign(distanceToPlane.fwidth().div(2.0)); intersectionClipOpacity.mulAssign((0, _MathNode.smoothstep)(distanceGradient.negate(), distanceGradient, distanceToPlane).oneMinus()); }); clipOpacity.mulAssign(intersectionClipOpacity.oneMinus()); } _PropertyNode.diffuseColor.a.mulAssign(clipOpacity); _PropertyNode.diffuseColor.a.equal(0.0).discard(); })(); } /** * Setups the default clipping. * * @param {Array<Vector4>} intersectionPlanes - The intersection planes. * @param {Array<Vector4>} unionPlanes - The union planes. * @return {Node} The result node. */ setupDefault(intersectionPlanes, unionPlanes) { return (0, _TSLBase.Fn)(() => { const numUnionPlanes = unionPlanes.length; if (this.hardwareClipping === false && numUnionPlanes > 0) { const clippingPlanes = (0, _UniformArrayNode.uniformArray)(unionPlanes); (0, _LoopNode.Loop)(numUnionPlanes, ({ i }) => { const plane = clippingPlanes.element(i); _Position.positionView.dot(plane.xyz).greaterThan(plane.w).discard(); }); } const numIntersectionPlanes = intersectionPlanes.length; if (numIntersectionPlanes > 0) { const clippingPlanes = (0, _UniformArrayNode.uniformArray)(intersectionPlanes); const clipped = (0, _TSLBase.bool)(true).toVar('clipped'); (0, _LoopNode.Loop)(numIntersectionPlanes, ({ i }) => { const plane = clippingPlanes.element(i); clipped.assign(_Position.positionView.dot(plane.xyz).greaterThan(plane.w).and(clipped)); }); clipped.discard(); } })(); } /** * Setups hardware clipping. * * @param {Array<Vector4>} unionPlanes - The union planes. * @param {NodeBuilder} builder - The current node builder. * @return {Node} The result node. */ setupHardwareClipping(unionPlanes, builder) { const numUnionPlanes = unionPlanes.length; builder.enableHardwareClipping(numUnionPlanes); return (0, _TSLBase.Fn)(() => { const clippingPlanes = (0, _UniformArrayNode.uniformArray)(unionPlanes); const hw_clip_distances = (0, _BuiltinNode.builtin)(builder.getClipDistance()); (0, _LoopNode.Loop)(numUnionPlanes, ({ i }) => { const plane = clippingPlanes.element(i); const distance = _Position.positionView.dot(plane.xyz).sub(plane.w).negate(); hw_clip_distances.element(i).assign(distance); }); })(); } } ClippingNode.ALPHA_TO_COVERAGE = 'alphaToCoverage'; ClippingNode.DEFAULT = 'default'; ClippingNode.HARDWARE = 'hardware'; var _default = exports.default = ClippingNode; /** * TSL function for setting up the default clipping logic. * * @tsl * @function * @returns {ClippingNode} */ const clipping = () => (0, _TSLBase.nodeObject)(new ClippingNode()); /** * TSL function for setting up alpha to coverage. * * @tsl * @function * @returns {ClippingNode} */ exports.clipping = clipping; const clippingAlpha = () => (0, _TSLBase.nodeObject)(new ClippingNode(ClippingNode.ALPHA_TO_COVERAGE)); /** * TSL function for setting up hardware-based clipping. * * @tsl * @function * @returns {ClippingNode} */ exports.clippingAlpha = clippingAlpha; const hardwareClipping = () => (0, _TSLBase.nodeObject)(new ClippingNode(ClippingNode.HARDWARE)); exports.hardwareClipping = hardwareClipping;