@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
230 lines (229 loc) • 7.91 kB
JavaScript
"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];
}
}
}