@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
340 lines (325 loc) • 12 kB
text/typescript
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>;