UNPKG

@polygonjs/polygonjs

Version:

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

230 lines (229 loc) 7.91 kB
"use strict"; import { Color } from "three"; import { CoreColor } from "../../../core/Color"; import { TypedSopNode } from "./_Base"; import { InputCloneMode } from "../../poly/InputCloneMode"; const DEFAULT_COLOR = new Color(1, 1, 1); const COLOR_ATTRIB_NAME = "color"; const _points = []; import { ColorSopOperation } from "../../operations/sop/Color"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { isBooleanTrue } from "../../../core/BooleanValue"; import { SopType } from "../../poly/registers/nodes/types/Sop"; import { pointsFromObject } from "../../../core/geometry/entities/point/CorePointUtils"; import { isObject3D } from "../../../core/geometry/ObjectContent"; import { corePointClassFactory } from "../../../core/geometry/CoreObjectFactory"; const DEFAULT = ColorSopOperation.DEFAULT_PARAMS; class ColorSopParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); /** @param toggle on if the color should be copied from another attribute */ this.fromAttribute = ParamConfig.BOOLEAN(DEFAULT.fromAttribute); /** @param attribute name to copy value from */ this.attribName = ParamConfig.STRING(DEFAULT.attribName, { visibleIf: { fromAttribute: 1 } }); /** @param color valu */ this.color = ParamConfig.COLOR(DEFAULT.color, { visibleIf: { fromAttribute: 0 }, expression: { forEntities: true } }); /** @param toggle on if the value should be set with hsv values rather than rgb */ this.asHsv = ParamConfig.BOOLEAN(DEFAULT.asHsv, { visibleIf: { fromAttribute: 0 } }); } } const ParamsConfig = new ColorSopParamsConfig(); export class ColorSopNode extends TypedSopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; this._arrayByGeometryUUID = { R: {}, G: {}, B: {} }; } static type() { return SopType.COLOR; } initializeNode() { this.io.inputs.setCount(1); this.io.inputs.initInputsClonedState(InputCloneMode.FROM_NODE); } async cook(inputCoreGroups) { const coreGroup = inputCoreGroups[0]; const objects = coreGroup.allObjects(); for (const object of objects) { if (isBooleanTrue(this.pv.fromAttribute)) { this._setFromAttribute(object); } else { const hasExpression = this.p.color.hasExpression(); if (hasExpression) { await this._evalExpressions(object); } else { this._evalSimpleValues(object); } } } if (!this.io.inputs.cloneRequired(0)) { const geometries = coreGroup.geometries(); for (const geometry of geometries) { geometry.getAttribute(COLOR_ATTRIB_NAME).needsUpdate = true; } } this.setCoreGroup(coreGroup); } _setFromAttribute(object) { const corePointClass = corePointClassFactory(object); const attribName = this.pv.attribName; if (attribName.trim().length == 0) { return; } const srcAttrib = corePointClass.attribute(object, attribName); if (!srcAttrib) { return; } this._createInitColor(object); pointsFromObject(object, _points); const pointsCount = _points.length; const srcAttribSize = corePointClass.attribSize(object, attribName); const srcArray = srcAttrib.array; const destArray = corePointClass.attribute(object, COLOR_ATTRIB_NAME).array; switch (srcAttribSize) { case 1: { for (let i = 0; i < pointsCount; i++) { const dest_i = i * 3; destArray[dest_i + 0] = srcArray[i]; destArray[dest_i + 1] = 1 - srcArray[i]; destArray[dest_i + 2] = 0; } break; } case 2: { for (let i = 0; i < pointsCount; i++) { const dest_i = i * 3; const src_i = i * 2; destArray[dest_i + 0] = srcArray[src_i + 0]; destArray[dest_i + 1] = srcArray[src_i + 1]; destArray[dest_i + 2] = 0; } break; } case 3: { for (let i = 0; i < srcArray.length; i++) { destArray[i] = srcArray[i]; } break; } case 4: { for (let i = 0; i < pointsCount; i++) { const dest_i = i * 3; const src_i = i * 4; destArray[dest_i + 0] = srcArray[src_i + 0]; destArray[dest_i + 1] = srcArray[src_i + 1]; destArray[dest_i + 2] = srcArray[src_i + 2]; } break; } } } _createInitColor(object) { const corePointClass = corePointClassFactory(object); if (!corePointClass.hasAttribute(object, COLOR_ATTRIB_NAME)) { corePointClass.addNumericAttribute(object, COLOR_ATTRIB_NAME, 3, DEFAULT_COLOR); } } _evalSimpleValues(object) { const corePointClass = corePointClassFactory(object); this._createInitColor(object); let newColor; if (isBooleanTrue(this.pv.asHsv)) { newColor = new Color(); CoreColor.setHSV(this.pv.color.r, this.pv.color.g, this.pv.color.b, newColor); } else { newColor = this.pv.color; } corePointClass.addNumericAttribute(object, COLOR_ATTRIB_NAME, 3, newColor); } async _evalExpressions(object) { const points = []; pointsFromObject(object, points); if (!isObject3D(object)) { return; } this._createInitColor(object); const geometry = object.geometry; if (geometry) { const array = geometry.getAttribute(COLOR_ATTRIB_NAME).array; const tmpArrayR = await this._updateFromParam(geometry, array, points, 0); const tmpArrayG = await this._updateFromParam(geometry, array, points, 1); const tmpArrayB = await this._updateFromParam(geometry, array, points, 2); if (tmpArrayR) { this._commitTmpValues(tmpArrayR, array, 0); } if (tmpArrayG) { this._commitTmpValues(tmpArrayG, array, 1); } if (tmpArrayB) { this._commitTmpValues(tmpArrayB, array, 2); } if (isBooleanTrue(this.pv.asHsv)) { let current = new Color(); let target = new Color(); let index; for (const point of points) { index = point.index() * 3; current.fromArray(array, index); CoreColor.setHSV(current.r, current.g, current.b, target); target.toArray(array, index); } } } } async _updateFromParam(geometry, array, points, offset) { const param = this.p.color.components[offset]; const paramValue = [this.pv.color.r, this.pv.color.g, this.pv.color.b][offset]; const arraysByGeometryUUID = [ this._arrayByGeometryUUID.R, this._arrayByGeometryUUID.B, this._arrayByGeometryUUID.G ][offset]; let tmpArray; if (param.hasExpression() && param.expressionController) { tmpArray = this._initArrayIfRequired(geometry, arraysByGeometryUUID, points.length); if (param.expressionController.entitiesDependent()) { await param.expressionController.computeExpressionForPoints(points, (point, value) => { tmpArray[point.index()] = value; }); } else { for (const point of points) { tmpArray[point.index()] = param.value; } } } else { for (const point of points) { array[point.index() * 3 + offset] = paramValue; } } return tmpArray; } _initArrayIfRequired(geometry, arraysByGeometryUUID, pointsCount) { const uuid = geometry.uuid; const currentArray = arraysByGeometryUUID[uuid]; if (currentArray) { if (currentArray.length < pointsCount) { arraysByGeometryUUID[uuid] = new Array(pointsCount); } } else { arraysByGeometryUUID[uuid] = new Array(pointsCount); } return arraysByGeometryUUID[uuid]; } _commitTmpValues(tmpArray, targetArray, offset) { for (let i = 0; i < tmpArray.length; i++) { targetArray[i * 3 + offset] = tmpArray[i]; } } }