polygonjs-engine
Version:
node-based webgl 3D engine https://polygonjs.com
162 lines (138 loc) • 5.28 kB
text/typescript
import {BaseSopOperation} from './_Base';
import {CoreGroup} from '../../geometry/Group';
import {DefaultOperationParams} from '../_Base';
import {BufferGeometry} from 'three/src/core/BufferGeometry';
import {BufferAttribute} from 'three/src/core/BufferAttribute';
import {ObjectType} from '../../../core/geometry/Constant';
import {InputCloneMode} from '../../../engine/poly/InputCloneMode';
import {CoreIterator} from '../../Iterator';
import {CoreMath} from '../../math/_Module';
import {CoreType} from '../../Type';
import {ArrayUtils} from '../../ArrayUtils';
interface ScatterSopParams extends DefaultOperationParams {
pointsCount: number;
seed: number;
transferAttributes: boolean;
attributesToTransfer: string;
addIdAttribute: boolean;
addIdnAttribute: boolean;
}
export class ScatterSopOperation extends BaseSopOperation {
static readonly DEFAULT_PARAMS: ScatterSopParams = {
pointsCount: 100,
seed: 0,
transferAttributes: true,
attributesToTransfer: 'normal',
addIdAttribute: true,
addIdnAttribute: true,
};
static readonly INPUT_CLONED_STATE = InputCloneMode.FROM_NODE;
static type(): Readonly<'scatter'> {
return 'scatter';
}
async cook(input_contents: CoreGroup[], params: ScatterSopParams) {
const core_group = input_contents[0];
let faces = core_group.faces();
const areas_thresholds: number[] = [];
let area_sum = 0;
const area_by_face_index: Map<number, number> = new Map();
for (let face of faces) {
const area = face.area();
area_by_face_index.set(face.index(), area);
}
const sorted_faces = ArrayUtils.sortBy(faces, (f) => {
return area_by_face_index.get(f.index()) || -1;
});
let i = 0;
for (let face of sorted_faces) {
area_sum += area_by_face_index.get(face.index()) as number;
areas_thresholds[i] = area_sum;
i++;
}
const positions: number[] = [];
let attrib_names: string[] = [];
if (params.transferAttributes) {
attrib_names = core_group.attribNamesMatchingMask(params.attributesToTransfer);
}
const attrib_values_by_name: Map<string, number[]> = new Map();
const attrib_sizes_by_name: Map<string, number> = new Map();
for (let attrib_name of attrib_names) {
attrib_values_by_name.set(attrib_name, []);
attrib_sizes_by_name.set(attrib_name, core_group.attribSize(attrib_name));
}
const iterator = new CoreIterator();
// await iterator.start_with_count(params.pointsCount, this._add_point.bind(this))
await iterator.start_with_count(params.pointsCount, (point_index: number) => {
const rand = CoreMath.rand_float(params.seed + point_index) * area_sum;
for (let face_index = 0; face_index < areas_thresholds.length; face_index++) {
const areas_threshold = areas_thresholds[face_index];
if (rand <= areas_threshold) {
const face = sorted_faces[face_index];
const position = face.random_position(rand);
position.toArray(positions, positions.length);
for (let attrib_name of attrib_names) {
const attrib_value = face.attrib_value_at_position(attrib_name, position);
if (attrib_value) {
if (CoreType.isNumber(attrib_value)) {
attrib_values_by_name.get(attrib_name)!.push(attrib_value);
} else {
attrib_value.toArray(
attrib_values_by_name.get(attrib_name),
attrib_values_by_name.get(attrib_name)!.length
);
}
}
}
break;
}
}
});
// for(let point_index=0; point_index<params.pointsCount; point_index++){
// const rand = CoreMath.rand_float(params.seed+point_index) * area_sum
// for(let face_index=0; face_index<areas_thresholds.length; face_index++){
// const areas_threshold = areas_thresholds[face_index]
// if(rand <= areas_threshold){
// const face = sorted_faces[face_index]
// const position = face.random_position(rand)
// position.toArray(positions, positions.length)
// for(let attrib_name of attrib_names){
// const attrib_value = face.attrib_value_at_position(attrib_name, position)
// if (CoreType.isNumber(attrib_value)){
// attrib_values_by_name[attrib_name].push(attrib_value)
// } else {
// attrib_value.toArray(
// attrib_values_by_name[attrib_name],
// attrib_values_by_name[attrib_name].length
// )
// }
// }
// break;
// }
// }
// }
const geometry = new BufferGeometry();
geometry.setAttribute('position', new BufferAttribute(new Float32Array(positions), 3));
for (let attrib_name of attrib_names) {
geometry.setAttribute(
attrib_name,
new BufferAttribute(
new Float32Array(attrib_values_by_name.get(attrib_name)!),
attrib_sizes_by_name.get(attrib_name)!
)
);
}
if (params.addIdAttribute || params.addIdnAttribute) {
const pointsCount = params.pointsCount;
const ids = ArrayUtils.range(pointsCount);
if (params.addIdAttribute) {
geometry.setAttribute('id', new BufferAttribute(new Float32Array(ids), 1));
}
const idns = ids.map((id) => id / (pointsCount - 1));
if (params.addIdnAttribute) {
geometry.setAttribute('idn', new BufferAttribute(new Float32Array(idns), 1));
}
}
const object = this.create_object(geometry, ObjectType.POINTS);
return this.create_core_group_from_objects([object]);
}
}