UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

157 lines (148 loc) 5.05 kB
import NodeMaterial from './NodeMaterial.js'; import { cameraProjectionMatrix } from '../../nodes/accessors/Camera.js'; import { materialRotation } from '../../nodes/accessors/MaterialNode.js'; import { modelViewMatrix, modelWorldMatrix } from '../../nodes/accessors/ModelNode.js'; import { positionGeometry } from '../../nodes/accessors/Position.js'; import { rotate } from '../../nodes/utils/RotateNode.js'; import { float, vec2, vec3, vec4 } from '../../nodes/tsl/TSLBase.js'; import { SpriteMaterial } from '../SpriteMaterial.js'; import { reference } from '../../nodes/accessors/ReferenceBaseNode.js'; const _defaultValues = /*@__PURE__*/new SpriteMaterial(); /** * Node material version of {@link SpriteMaterial}. * * @augments NodeMaterial */ class SpriteNodeMaterial extends NodeMaterial { static get type() { return 'SpriteNodeMaterial'; } /** * Constructs a new sprite node material. * * @param {Object} [parameters] - The configuration parameter. */ constructor(parameters) { super(); /** * This flag can be used for type testing. * * @type {boolean} * @readonly * @default true */ this.isSpriteNodeMaterial = true; this._useSizeAttenuation = true; /** * This property makes it possible to define the position of the sprite with a * node. That can be useful when the material is used with instanced rendering * and node data are defined with an instanced attribute node: * ```js * const positionAttribute = new InstancedBufferAttribute( new Float32Array( positions ), 3 ); * material.positionNode = instancedBufferAttribute( positionAttribute ); * ``` * Another possibility is to compute the instanced data with a compute shader: * ```js * const positionBuffer = instancedArray( particleCount, 'vec3' ); * particleMaterial.positionNode = positionBuffer.toAttribute(); * ``` * * @type {?Node<vec2>} * @default null */ this.positionNode = null; /** * The rotation of sprite materials is by default inferred from the `rotation`, * property. This node property allows to overwrite the default and define * the rotation with a node instead. * * If you don't want to overwrite the rotation but modify the existing * value instead, use {@link materialRotation}. * * @type {?Node<float>} * @default null */ this.rotationNode = null; /** * This node property provides an additional way to scale sprites next to * `Object3D.scale`. The scale transformation based in `Object3D.scale` * is multiplied with the scale value of this node in the vertex shader. * * @type {?Node<vec2>} * @default null */ this.scaleNode = null; /** * In Sprites, the transparent property is enabled by default. * * @type {boolean} * @default true */ this.transparent = true; this.setDefaultValues(_defaultValues); this.setValues(parameters); } /** * Setups the position node in view space. This method implements * the sprite specific vertex shader. * * @param {NodeBuilder} builder - The current node builder. * @return {Node<vec3>} The position in view space. */ setupPositionView(builder) { const { object, camera } = builder; const sizeAttenuation = this.sizeAttenuation; const { positionNode, rotationNode, scaleNode } = this; const mvPosition = modelViewMatrix.mul(vec3(positionNode || 0)); let scale = vec2(modelWorldMatrix[0].xyz.length(), modelWorldMatrix[1].xyz.length()); if (scaleNode !== null) { scale = scale.mul(vec2(scaleNode)); } if (sizeAttenuation === false) { if (camera.isPerspectiveCamera) { scale = scale.mul(mvPosition.z.negate()); } else { const orthoScale = float(2.0).div(cameraProjectionMatrix.element(1).element(1)); scale = scale.mul(orthoScale.mul(2)); } } let alignedPosition = positionGeometry.xy; if (object.center && object.center.isVector2 === true) { const center = reference('center', 'vec2', object); alignedPosition = alignedPosition.sub(center.sub(0.5)); } alignedPosition = alignedPosition.mul(scale); const rotation = float(rotationNode || materialRotation); const rotatedPosition = rotate(alignedPosition, rotation); return vec4(mvPosition.xy.add(rotatedPosition), mvPosition.zw); } copy(source) { this.positionNode = source.positionNode; this.rotationNode = source.rotationNode; this.scaleNode = source.scaleNode; return super.copy(source); } /** * Whether to use size attenuation or not. * * @type {boolean} * @default true */ get sizeAttenuation() { return this._useSizeAttenuation; } set sizeAttenuation(value) { if (this._useSizeAttenuation !== value) { this._useSizeAttenuation = value; this.needsUpdate = true; } } } export default SpriteNodeMaterial;