polygonjs-engine
Version:
node-based webgl 3D engine https://polygonjs.com
239 lines (219 loc) • 7.79 kB
text/typescript
/**
* Applies multiple rotations with one node
*
*
*
*/
import {TypedSopNode} from './_Base';
import {CoreGroup, Object3DWithGeometry} from '../../../core/geometry/Group';
import {
CoreTransform,
ROTATION_ORDERS,
RotationOrder,
TransformTargetType,
TRANSFORM_TARGET_TYPES,
} from '../../../core/Transform';
import {InputCloneMode} from '../../poly/InputCloneMode';
import {Vector3} from 'three/src/math/Vector3';
import {ParamOptions, MenuNumericParamOptions, VisibleIfParamOptions} from '../../params/utils/OptionsController';
const max_transform_count = 6;
const ROT_ORDER_DEFAULT = ROTATION_ORDERS.indexOf(RotationOrder.XYZ);
const ROT_ORDER_MENU_ENTRIES: MenuNumericParamOptions = {
menu: {
entries: ROTATION_ORDERS.map((order, v) => {
return {name: order, value: v};
}),
},
};
function visible_for_count(count: number): ParamOptions {
const list: VisibleIfParamOptions[] = [];
for (let i = count + 1; i <= max_transform_count; i++) {
list.push({
count: i,
});
}
return {visibleIf: list};
}
type VectorNumberParamPair = [Vector3Param, IntegerParam];
import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
import {IntegerParam} from '../../params/Integer';
import {Vector3Param} from '../../params/Vector3';
import {TypeAssert} from '../../poly/Assert';
import {Object3D} from 'three/src/core/Object3D';
import {BufferAttribute} from 'three/src/core/BufferAttribute';
import {CoreAttribute, Attribute} from '../../../core/geometry/Attribute';
class TransformMultiSopParamConfig extends NodeParamsConfig {
/** @param defines if this applies to objects or geometries */
applyOn = ParamConfig.INTEGER(TRANSFORM_TARGET_TYPES.indexOf(TransformTargetType.GEOMETRIES), {
menu: {
entries: TRANSFORM_TARGET_TYPES.map((target_type, i) => {
return {name: target_type, value: i};
}),
},
});
/** @param number of transformations this can apply */
count = ParamConfig.INTEGER(2, {
range: [0, max_transform_count],
rangeLocked: [true, true],
});
// 0
sep0 = ParamConfig.SEPARATOR(null, {...visible_for_count(0)});
/** @param transform 0 rotation order */
rotationOrder0 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
...ROT_ORDER_MENU_ENTRIES,
...visible_for_count(0),
});
/** @param rotation 0 */
r0 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(0)});
// 1
sep1 = ParamConfig.SEPARATOR(null, {...visible_for_count(1)});
/** @param transform 1 rotation order */
rotationOrder1 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
...ROT_ORDER_MENU_ENTRIES,
...visible_for_count(1),
});
/** @param rotation 1 */
r1 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(1)});
// 2
sep2 = ParamConfig.SEPARATOR(null, {...visible_for_count(2)});
/** @param transform 2 rotation order */
rotationOrder2 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
...ROT_ORDER_MENU_ENTRIES,
...visible_for_count(2),
});
/** @param rotation 2 */
r2 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(2)});
// 3
sep3 = ParamConfig.SEPARATOR(null, {...visible_for_count(3)});
/** @param transform 3 rotation order */
rotationOrder3 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
...ROT_ORDER_MENU_ENTRIES,
...visible_for_count(3),
});
/** @param rotation 3 */
r3 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(3)});
// 4
sep4 = ParamConfig.SEPARATOR(null, {...visible_for_count(4)});
/** @param transform 4 rotation order */
rotationOrder4 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
...ROT_ORDER_MENU_ENTRIES,
...visible_for_count(4),
});
/** @param rotation 4 */
r4 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(4)});
// 5
sep5 = ParamConfig.SEPARATOR(null, {...visible_for_count(5)});
/** @param transform 5 rotation order */
rotationOrder5 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
...ROT_ORDER_MENU_ENTRIES,
...visible_for_count(5),
});
/** @param rotation 5 */
r5 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(5)});
}
const ParamsConfig = new TransformMultiSopParamConfig();
export class TransformMultiSopNode extends TypedSopNode<TransformMultiSopParamConfig> {
params_config = ParamsConfig;
static type() {
return 'transformMulti';
}
static displayedInputNames(): string[] {
return ['objects to transform', 'objects to copy initial transform from'];
}
initializeNode() {
this.io.inputs.setCount(1, 2);
this.io.inputs.initInputsClonedState([InputCloneMode.FROM_NODE, InputCloneMode.NEVER]);
this.scene().dispatchController.onAddListener(() => {
this.params.onParamsCreated('params_label', () => {
this.params.label.init([this.p.applyOn], () => {
return TRANSFORM_TARGET_TYPES[this.pv.applyOn];
});
});
});
this.params.onParamsCreated('cache param pairs', () => {
this._rot_and_index_pairs = [
[this.p.r0, this.p.rotationOrder0],
[this.p.r1, this.p.rotationOrder1],
[this.p.r2, this.p.rotationOrder2],
[this.p.r3, this.p.rotationOrder3],
[this.p.r4, this.p.rotationOrder4],
[this.p.r5, this.p.rotationOrder5],
];
});
}
private _core_transform = new CoreTransform();
private _rot_and_index_pairs: VectorNumberParamPair[] | undefined;
cook(input_contents: CoreGroup[]) {
const objects = input_contents[0].objectsWithGeo();
const src_object = input_contents[1] ? input_contents[1].objectsWithGeo()[0] : undefined;
this._apply_transforms(objects, src_object);
this.setObjects(objects);
}
private _apply_transforms(objects: Object3DWithGeometry[], src_object: Object3DWithGeometry | undefined) {
const mode = TRANSFORM_TARGET_TYPES[this.pv.applyOn];
switch (mode) {
case TransformTargetType.GEOMETRIES: {
return this._apply_matrix_to_geometries(objects, src_object);
}
case TransformTargetType.OBJECTS: {
return this._apply_matrix_to_objects(objects, src_object);
}
}
TypeAssert.unreachable(mode);
}
private _apply_matrix_to_geometries(objects: Object3DWithGeometry[], src_object: Object3DWithGeometry | undefined) {
if (!this._rot_and_index_pairs) {
return;
}
if (src_object) {
const src_geometry = src_object.geometry;
if (src_geometry) {
const attributes_to_copy = [Attribute.POSITION, Attribute.NORMAL, Attribute.TANGENT];
for (let attrib_name of attributes_to_copy) {
const src = src_geometry.attributes[attrib_name] as BufferAttribute | null;
for (let object of objects) {
const geometry = object.geometry;
const dest = geometry.attributes[attrib_name] as BufferAttribute | null;
if (src && dest) {
CoreAttribute.copy(src, dest);
}
}
}
}
}
let pair: VectorNumberParamPair;
for (let i = 0; i < this.pv.count; i++) {
pair = this._rot_and_index_pairs[i];
const matrix = this._matrix(pair[0].value, pair[1].value);
for (let object of objects) {
object.geometry.applyMatrix4(matrix);
}
}
}
private _apply_matrix_to_objects(objects: Object3D[], src_object: Object3D | undefined) {
if (!this._rot_and_index_pairs) {
return;
}
if (src_object) {
for (let object of objects) {
object.matrix.copy(src_object.matrix);
// TODO: This would not be required if objects generated in SOP has matrixAutoUpdate=false
object.matrix.decompose(object.position, object.quaternion, object.scale);
}
}
let pair: VectorNumberParamPair;
for (let i = 0; i < this.pv.count; i++) {
pair = this._rot_and_index_pairs[i];
const matrix = this._matrix(pair[0].value, pair[1].value);
for (let object of objects) {
object.applyMatrix4(matrix);
}
}
}
private _t = new Vector3(0, 0, 0);
private _s = new Vector3(1, 1, 1);
private _scale = 1;
private _matrix(r: Vector3, rot_order_index: number) {
return this._core_transform.matrix(this._t, r, this._s, this._scale, ROTATION_ORDERS[rot_order_index]);
}
}