UNPKG

polygonjs-engine

Version:

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

300 lines (272 loc) 9.36 kB
/** * Set a vertex color attribute * * @remarks * Note that just like the attrib_create, it is possible to use an expression to set the attribute value * */ import {Color} from 'three/src/math/Color'; import {BufferAttribute} from 'three/src/core/BufferAttribute'; import {CoreColor} from '../../../core/Color'; import {TypedSopNode} from './_Base'; import {CoreObject} from '../../../core/geometry/Object'; import {CoreGeometry} from '../../../core/geometry/Geometry'; import {CorePoint} from '../../../core/geometry/Point'; import {CoreGroup} from '../../../core/geometry/Group'; import {InputCloneMode} from '../../poly/InputCloneMode'; import {BufferGeometry} from 'three/src/core/BufferGeometry'; import {Mesh} from 'three/src/objects/Mesh'; const DEFAULT_COLOR = new Color(1, 1, 1); const COLOR_ATTRIB_NAME = 'color'; type ValueArrayByName = PolyDictionary<number[]>; import {ColorSopOperation} from '../../../core/operations/sop/Color'; import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig'; import {PolyDictionary} from '../../../types/GlobalTypes'; const DEFAULT = ColorSopOperation.DEFAULT_PARAMS; class ColorSopParamsConfig extends NodeParamsConfig { /** @param toggle on if the color should be copied from another attribute */ fromAttribute = ParamConfig.BOOLEAN(DEFAULT.fromAttribute); /** @param attribute name to copy value from */ attribName = ParamConfig.STRING(DEFAULT.attribName, { visibleIf: {fromAttribute: 1}, }); /** @param color valu */ 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 */ asHsv = ParamConfig.BOOLEAN(DEFAULT.asHsv, { visibleIf: {fromAttribute: 0}, }); } const ParamsConfig = new ColorSopParamsConfig(); export class ColorSopNode extends TypedSopNode<ColorSopParamsConfig> { params_config = ParamsConfig; static type() { return 'color'; } private _r_arrays_by_geometry_uuid: ValueArrayByName = {}; private _g_arrays_by_geometry_uuid: ValueArrayByName = {}; private _b_arrays_by_geometry_uuid: ValueArrayByName = {}; static displayedInputNames(): string[] { return ['geometry to update color of']; } initializeNode() { this.io.inputs.setCount(1); this.io.inputs.initInputsClonedState(InputCloneMode.FROM_NODE); // this.uiData.set_icon('palette'); } async cook(input_contents: CoreGroup[]) { const core_group = input_contents[0]; const core_objects = core_group.coreObjects(); for (let core_object of core_objects) { if (this.pv.fromAttribute) { this._set_fromAttribute(core_object); } else { const has_expression = this.p.color.has_expression(); if (has_expression) { await this._eval_expressions(core_object); } else { this._eval_simple_values(core_object); } } } // needs update required for when no cloning if (!this.io.inputs.clone_required(0)) { const geometries = core_group.geometries(); for (let geometry of geometries) { (geometry.getAttribute(COLOR_ATTRIB_NAME) as BufferAttribute).needsUpdate = true; } } this.setCoreGroup(core_group); } _set_fromAttribute(core_object: CoreObject) { const core_geometry = core_object.coreGeometry(); if (!core_geometry) { return; } this._create_init_color(core_geometry, DEFAULT_COLOR); const points = core_geometry.points(); const src_attrib_size = core_geometry.attribSize(this.pv.attribName); const geometry = core_geometry.geometry(); const src_array = geometry.getAttribute(this.pv.attribName).array; const dest_array = geometry.getAttribute(COLOR_ATTRIB_NAME).array as number[]; switch (src_attrib_size) { case 1: { for (let i = 0; i < points.length; i++) { const dest_i = i * 3; dest_array[dest_i + 0] = src_array[i]; dest_array[dest_i + 1] = 1 - src_array[i]; dest_array[dest_i + 2] = 0; } break; } case 2: { for (let i = 0; i < points.length; i++) { const dest_i = i * 3; const src_i = i * 2; dest_array[dest_i + 0] = src_array[src_i + 0]; dest_array[dest_i + 1] = src_array[src_i + 1]; dest_array[dest_i + 2] = 0; } break; } case 3: { for (let i = 0; i < src_array.length; i++) { dest_array[i] = src_array[i]; } break; } case 4: { for (let i = 0; i < points.length; i++) { const dest_i = i * 3; const src_i = i * 4; dest_array[dest_i + 0] = src_array[src_i + 0]; dest_array[dest_i + 1] = src_array[src_i + 1]; dest_array[dest_i + 2] = src_array[src_i + 2]; } break; } } } private _create_init_color(core_geometry: CoreGeometry, color: Color) { if (!core_geometry.hasAttrib(COLOR_ATTRIB_NAME)) { core_geometry.addNumericAttrib(COLOR_ATTRIB_NAME, 3, DEFAULT_COLOR); } } _eval_simple_values(core_object: CoreObject) { const core_geometry = core_object.coreGeometry(); if (!core_geometry) { return; } this._create_init_color(core_geometry, DEFAULT_COLOR); let new_color: Color; if (this.pv.asHsv) { new_color = new Color(); CoreColor.set_hsv(this.pv.color.r, this.pv.color.g, this.pv.color.b, new_color); } else { new_color = this.pv.color; //.clone(); } core_geometry.addNumericAttrib(COLOR_ATTRIB_NAME, 3, new_color); } async _eval_expressions(core_object: CoreObject) { const points = core_object.points(); const object = core_object.object(); const core_geometry = core_object.coreGeometry(); if (core_geometry) { this._create_init_color(core_geometry, DEFAULT_COLOR); } const geometry = (object as Mesh).geometry as BufferGeometry; if (geometry) { const array = geometry.getAttribute(COLOR_ATTRIB_NAME).array as number[]; const tmp_array_r = await this._update_from_param(geometry, array, points, 0); const tmp_array_g = await this._update_from_param(geometry, array, points, 1); const tmp_array_b = await this._update_from_param(geometry, array, points, 2); if (tmp_array_r) { this._commit_tmp_values(tmp_array_r, array, 0); } if (tmp_array_g) { this._commit_tmp_values(tmp_array_g, array, 1); } if (tmp_array_b) { this._commit_tmp_values(tmp_array_b, array, 2); } // to hsv if (this.pv.asHsv) { let current = new Color(); let target = new Color(); let index; for (let point of points) { index = point.index() * 3; current.fromArray(array, index); CoreColor.set_hsv(current.r, current.g, current.b, target); target.toArray(array, index); } } } // const colorr_param = this.param('colorr'); // const colorg_param = this.param('colorg'); // const colorb_param = this.param('colorb'); // r // if(colorr_param.has_expression()){ // await colorr_param.eval_expression_for_entities(points, (point, value)=>{ // array[point.index()*3+0] = value // }) // } else { // for(let point of points){ // array[point.index()*3+0] = this.pv.color.r // } // } // g // if(colorg_param.has_expression()){ // await colorg_param.eval_expression_for_entities(points, (point, value)=>{ // array[point.index()*3+1] = value // }) // } else { // for(let point of points){ // array[point.index()*3+1] = this.pv.color.g // } // } // b // if(colorb_param.has_expression()){ // await colorb_param.eval_expression_for_entities(points, (point, value)=>{ // array[point.index()*3+2] = value // }) // } else { // for(let point of points){ // array[point.index()*3+2] = this.pv.color.b // } // } } private async _update_from_param( geometry: BufferGeometry, array: number[], points: CorePoint[], offset: number ): Promise<number[] | undefined> { // const component_name = ['r', 'g', 'b'][offset]; const param = this.p.color.components[offset]; const param_value = [this.pv.color.r, this.pv.color.g, this.pv.color.b][offset]; const arrays_by_geometry_uuid = [ this._r_arrays_by_geometry_uuid, this._g_arrays_by_geometry_uuid, this._b_arrays_by_geometry_uuid, ][offset]; let tmp_array: number[] | undefined; if (param.has_expression() && param.expression_controller) { tmp_array = this._init_array_if_required(geometry, arrays_by_geometry_uuid, points.length); await param.expression_controller.compute_expression_for_points(points, (point, value) => { // array[point.index()*3+2] = value (tmp_array as number[])[point.index()] = value; }); } else { for (let point of points) { array[point.index() * 3 + offset] = param_value; } } return tmp_array; } private _init_array_if_required( geometry: BufferGeometry, arrays_by_geometry_uuid: ValueArrayByName, points_count: number ) { const uuid = geometry.uuid; const current_array = arrays_by_geometry_uuid[uuid]; if (current_array) { // only create new array if we need more point, or as soon as the length is different? if (current_array.length < points_count) { arrays_by_geometry_uuid[uuid] = new Array(points_count); } } else { arrays_by_geometry_uuid[uuid] = new Array(points_count); } return arrays_by_geometry_uuid[uuid]; } private _commit_tmp_values(tmp_array: number[], target_array: number[], offset: number) { for (let i = 0; i < tmp_array.length; i++) { target_array[i * 3 + offset] = tmp_array[i]; } } }