UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

340 lines (325 loc) 12 kB
import {BaseJsNodeType} from '../_Base'; import {TypedJsDefinitionCollection} from './JsDefinitionCollection'; import {JsConnectionPointType} from '../../utils/io/connections/Js'; import {JsLinesCollectionController} from '../code/utils/JsLinesCollectionController'; import {nodeMethodName} from '../code/assemblers/actor/ActorAssemblerUtils'; import {LineType} from '../code/utils/LineType'; import {pushOnArrayAtEntry, mapGroupBy} from '../../../../core/MapUtils'; import {EvaluatorMethodName} from '../code/assemblers/actor/ActorEvaluator'; import {arrayUniq} from '../../../../core/ArrayUtils'; import {ActorAssemblerConstant} from '../code/assemblers/actor/ActorAssemblerCommon'; import {Poly} from '../../../Poly'; import {inputObject3D} from '../_BaseObject3D'; import {FUNC_POINTS_COUNT_FROM_OBJECT, FUNC_CORE_PRIMITIVE_CLASS_FACTORY} from './Common'; export enum JsDefinitionType { // ATTRIBUTE = 'attribute', LOCAL_FUNCTION = 'localFunction', COMPUTED = 'computed', CONSTANT = 'constant', REF = 'ref', WATCH = 'watch', INIT_FUNCTION = 'initFunction', TRIGGERING = 'triggering', TRIGGERABLE = 'triggerable', } export abstract class TypedJsDefinition<T extends JsDefinitionType> { constructor( protected _definitionType: T, protected _node: BaseJsNodeType, protected _shaderCollectionController: JsLinesCollectionController, protected _dataType: JsConnectionPointType, protected _name: string ) { // super(_node, _name); } static gather(definitions: BaseJsDefinition[], linesForShader: Map<LineType, string[]>, lineType: LineType) {} definitionType() { return this._definitionType; } dataType() { return this._dataType; } node() { return this._node; } name() { return this._name; } abstract line(): string; collectionInstance() { return new TypedJsDefinitionCollection<T>(); } } // export class AttributeGLDefinition extends TypedJsDefinition<JsDefinitionType.ATTRIBUTE> { // constructor( // protected override _node: BaseJsNodeType, // protected override _data_type: JsConnectionPointType, // protected override _name: string // ) { // super(JsDefinitionType.ATTRIBUTE, _data_type, _node, _name); // } // get line() { // return `attribute ${this.data_type} ${this.name()}`; // } // } export class LocalFunctionJsDefinition extends TypedJsDefinition<JsDefinitionType.LOCAL_FUNCTION> { constructor( protected override _node: BaseJsNodeType, protected override _shaderCollectionController: JsLinesCollectionController, protected override _dataType: JsConnectionPointType, protected override _name: string, protected _functionDefinition: string ) { super(JsDefinitionType.LOCAL_FUNCTION, _node, _shaderCollectionController, _dataType, _name); } line() { return this._functionDefinition; } // functionDefinition() { // return this._functionDefinition; // } } // export class UniformJsDefinition extends TypedJsDefinition<JsDefinitionType.UNIFORM> { // constructor( // protected override _node: BaseJsNodeType, // protected override _data_type: JsConnectionPointType, // protected override _name: string // ) { // super(JsDefinitionType.UNIFORM, _data_type, _node, _name); // } // get line() { // return `uniform ${this.data_type} ${this.name()}`; // } // } // export class InitJsDefinition extends TypedJsDefinition<JsDefinitionType.INIT> { // constructor( // protected override _node: BaseJsNodeType, // protected override _shaderCollectionController: ShadersCollectionController, // protected override _dataType: JsConnectionPointType, // protected override _name: string, // protected _value: string // ) { // super(JsDefinitionType.INIT, _node, _shaderCollectionController, _dataType, _name); // _shaderCollectionController.addComputedVarName(this.name()); // } // line() { // return ` this.${this.name()} = computed(()=> ${this._value} )`; // } // } export class ComputedValueJsDefinition extends TypedJsDefinition<JsDefinitionType.COMPUTED> { constructor( protected override _node: BaseJsNodeType, protected override _shaderCollectionController: JsLinesCollectionController, protected override _dataType: JsConnectionPointType, protected override _name: string, protected _value: string ) { super(JsDefinitionType.COMPUTED, _node, _shaderCollectionController, _dataType, _name); _shaderCollectionController.addComputedVarName(this.name()); } line() { return ` ${this.name()} = computed(()=> ${this._value} )`; } } export class ConstantJsDefinition extends TypedJsDefinition<JsDefinitionType.CONSTANT> { constructor( protected override _node: BaseJsNodeType, protected override _shaderCollectionController: JsLinesCollectionController, protected override _dataType: JsConnectionPointType, protected override _name: string, protected _value: string ) { super(JsDefinitionType.CONSTANT, _node, _shaderCollectionController, _dataType, _name); if (this._shaderCollectionController.assembler().computedVariablesAllowed()) { _shaderCollectionController.addComputedVarName(this.name()); } } line() { if (this._shaderCollectionController.assembler().computedVariablesAllowed()) { return ` ${this.name()} = {value:${this._value}}`; } else { return ` ${this.name()} = ${this._value}`; } } } export class RefJsDefinition extends TypedJsDefinition<JsDefinitionType.REF> { constructor( protected override _node: BaseJsNodeType, protected override _shaderCollectionController: JsLinesCollectionController, protected override _dataType: JsConnectionPointType, protected override _name: string, protected _value: string ) { super(JsDefinitionType.REF, _node, _shaderCollectionController, _dataType, _name); _shaderCollectionController.addComputedVarName(this.name()); } line() { return ` ${this.name()} = ref(${this._value})`; } } interface WatchedValueJsDefinitionOptions { // onChange: (prevVal: string) => string; deep?: boolean; } export class WatchedValueJsDefinition extends TypedJsDefinition<JsDefinitionType.WATCH> { constructor( protected override _node: BaseJsNodeType, protected override _shaderCollectionController: JsLinesCollectionController, protected override _dataType: JsConnectionPointType, protected override _name: string, protected _value: string, protected _options: WatchedValueJsDefinitionOptions ) { super(JsDefinitionType.WATCH, _node, _shaderCollectionController, _dataType, _name); _shaderCollectionController.addComputedVarName(this.name()); } line() { const deep = this._options.deep != null ? this._options.deep : false; return ` this._watchStopHandles.push( watch( ${this.name()}, ( )=> { ${this._value} }, { deep: ${deep} } ) )`; } } export class InitFunctionJsDefinition extends TypedJsDefinition<JsDefinitionType.INIT_FUNCTION> { constructor( protected override _node: BaseJsNodeType, protected override _shaderCollectionController: JsLinesCollectionController, protected override _dataType: JsConnectionPointType, protected override _name: string, protected _value: string ) { super(JsDefinitionType.INIT_FUNCTION, _node, _shaderCollectionController, _dataType, _name); // _shaderCollectionController.addComputedVarName(this.name()); } line() { return ` ${this._value}`; } } interface TriggeringJsDefinitionOptions { triggeringMethodName: EvaluatorMethodName; gatherable: boolean; nodeMethodName?: string; perPoint?: boolean; } export class TriggeringJsDefinition extends TypedJsDefinition<JsDefinitionType.TRIGGERING> { constructor( protected override _node: BaseJsNodeType, protected override _shaderCollectionController: JsLinesCollectionController, protected override _dataType: JsConnectionPointType, protected override _name: string, protected _value: string, protected _options: TriggeringJsDefinitionOptions ) { super(JsDefinitionType.TRIGGERING, _node, _shaderCollectionController, _dataType, _name); // _shaderCollectionController.addComputedVarName(this.name()); } override name() { return this._options.nodeMethodName || nodeMethodName(this._node); } line() { const methodName = this._options.nodeMethodName || nodeMethodName(this._node); if (this._options.perPoint) { const func = Poly.namedFunctionsRegister.getFunction( 'setPointIndex', this._node, this._shaderCollectionController ); const setPointIndex = func.asString(inputObject3D(this._node, this._shaderCollectionController), 'i'); return `${methodName}(){ const pointsCount = ${FUNC_POINTS_COUNT_FROM_OBJECT}(${ActorAssemblerConstant.OBJECT_3D}); for( let i = 0; i < pointsCount; i++ ) { ${setPointIndex} ${this._value} } if( ${ActorAssemblerConstant.OBJECT_3D}.isMesh ){ ${FUNC_CORE_PRIMITIVE_CLASS_FACTORY}(${ActorAssemblerConstant.OBJECT_3D}).computeVertexNormalsIfAttributeVersionChanged(${ActorAssemblerConstant.GEOMETRY}); } }`; } else { return `${methodName}(){ ${this._value} }`; } } static override gather( _definitions: TypedJsDefinition<JsDefinitionType>[], linesForShader: Map<LineType, string[]>, lineType: LineType ) { const triggeringDefinitions = ( _definitions.filter((d) => d.definitionType() == JsDefinitionType.TRIGGERING) as TriggeringJsDefinition[] ).filter((d) => d._options.gatherable == true); const definitionGroups = mapGroupBy( triggeringDefinitions, (definition) => definition._options.triggeringMethodName ); definitionGroups.forEach((definitions, triggeringMethodName) => { const uniqFunctionCalls: string[] = []; arrayUniq( definitions.map((d) => `this.${d.name()}()`), uniqFunctionCalls ); const definitionMethodCalls = uniqFunctionCalls.join(';'); const line = `${triggeringMethodName}(){ ${definitionMethodCalls} }`; pushOnArrayAtEntry(linesForShader, lineType, line); }); } } export interface TriggerableJsDefinitionOptions { async?: boolean; methodName?: string; } export class TriggerableJsDefinition extends TypedJsDefinition<JsDefinitionType.TRIGGERABLE> { constructor( protected override _node: BaseJsNodeType, protected override _shaderCollectionController: JsLinesCollectionController, protected override _dataType: JsConnectionPointType, protected override _name: string, protected _value: string, protected _options?: TriggerableJsDefinitionOptions ) { super(JsDefinitionType.TRIGGERABLE, _node, _shaderCollectionController, _dataType, _name); // _shaderCollectionController.addComputedVarName(this.name()); } line() { const _async = this._options?.async == true; const functionPrefix = _async ? 'async' : ''; const methodName = this._options?.methodName != null ? this._options?.methodName : nodeMethodName(this._node); return `${functionPrefix} ${methodName}(){ ${this._value} }`; } } // type DefinitionTypeMapGeneric = {[key in JsDefinitionType]: any}; export interface DefinitionTypeMap { [JsDefinitionType.LOCAL_FUNCTION]: typeof LocalFunctionJsDefinition; [JsDefinitionType.COMPUTED]: typeof ComputedValueJsDefinition; [JsDefinitionType.CONSTANT]: typeof ConstantJsDefinition; [JsDefinitionType.REF]: typeof RefJsDefinition; [JsDefinitionType.WATCH]: typeof WatchedValueJsDefinition; [JsDefinitionType.INIT_FUNCTION]: typeof InitFunctionJsDefinition; [JsDefinitionType.TRIGGERING]: typeof TriggeringJsDefinition; [JsDefinitionType.TRIGGERABLE]: typeof TriggerableJsDefinition; } export const JsDefinitionTypeMap: DefinitionTypeMap = { [JsDefinitionType.LOCAL_FUNCTION]: LocalFunctionJsDefinition, [JsDefinitionType.COMPUTED]: ComputedValueJsDefinition, [JsDefinitionType.CONSTANT]: ConstantJsDefinition, [JsDefinitionType.REF]: RefJsDefinition, [JsDefinitionType.WATCH]: WatchedValueJsDefinition, [JsDefinitionType.INIT_FUNCTION]: InitFunctionJsDefinition, [JsDefinitionType.TRIGGERING]: TriggeringJsDefinition, [JsDefinitionType.TRIGGERABLE]: TriggerableJsDefinition, }; export type BaseJsDefinition = TypedJsDefinition<JsDefinitionType>;