@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
159 lines (144 loc) • 3.73 kB
text/typescript
import {
Color,
Vector2,
Vector3,
Vector4,
BufferAttribute,
InstancedBufferAttribute,
InterleavedBufferAttribute,
} from 'three';
import {AttribValue, PolyDictionary} from '../../types/GlobalTypes';
import {arrayUniq} from '../ArrayUtils';
import {stringToAttribNames, stringMatchMask} from '../String';
import {isString,isNumber,isArray} from '../Type';
import {AttribSize, GroupString} from './Constant';
export enum Attribute {
POINT_INDEX = 'ptnum',
VERTEX_INDEX = 'vtxnum',
PRIMITIVE_INDEX = 'primnum',
OBJECT_INDEX = 'objnum',
OBJECT_NAME = 'objname',
COLOR = 'color',
NORMAL = 'normal',
POSITION = 'position',
PSCALE = 'pscale',
UP = 'up',
UV = 'uv',
SCALE = 'scale',
TANGENT = 'tangent',
ID = 'id',
}
export enum ObjectAttribute {
HOVERED = 'hovered',
}
const ATTRIB_NAME_MAP: PolyDictionary<string> = {
P: Attribute.POSITION,
N: Attribute.NORMAL,
Cd: Attribute.COLOR,
};
const _matchingAttribNames: string[] = [];
const _masks: string[] = [];
export class CoreAttribute {
static remapName(name: string): string {
return ATTRIB_NAME_MAP[name] || name;
}
static arrayToIndexedArrays(array: string[]) {
const index_by_value: PolyDictionary<number> = {};
let current_index = 0;
const indices = [];
const values = [];
let i = 0;
while (i < array.length) {
//(value = array[i++])?
const value = array[i];
const index = index_by_value[value];
if (index != null) {
indices.push(index);
} else {
values.push(value);
indices.push(current_index);
index_by_value[value] = current_index;
current_index += 1;
}
i++;
}
return {
indices,
values,
};
}
static defaultValue(size: number) {
switch (size) {
case 1:
return 0;
case 2:
return new Vector2(0, 0);
case 3:
return new Vector3(0, 0, 0);
default:
throw `size ${size} not yet implemented`;
}
}
static copy(src: BufferAttribute, dest: BufferAttribute, markAsNeedsUpdate = true) {
const srcArray = src?.array;
const destArray = dest?.array;
if (srcArray && destArray) {
const min_length = Math.min(srcArray.length, destArray.length);
for (let i = 0; i < min_length; i++) {
destArray[i] = srcArray[i];
}
if (markAsNeedsUpdate) {
dest.needsUpdate = true;
}
}
}
static attribSizeFromValue(val: AttribValue): AttribSize | null {
if (isString(val) || isNumber(val)) {
return AttribSize.FLOAT;
}
if (isArray(val)) {
return val.length;
}
switch (val.constructor) {
case Color:
return AttribSize.VECTOR3;
case Vector2:
return AttribSize.VECTOR2;
case Vector3:
return AttribSize.VECTOR3;
case Vector4:
return AttribSize.VECTOR4;
}
return null;
}
static attribNamesMatchingMask(masksString: GroupString, existingAttribNames: string[]) {
stringToAttribNames(masksString, _masks);
_matchingAttribNames.length = 0;
for (const mask of _masks) {
for (const attribName of existingAttribNames) {
if (stringMatchMask(attribName, mask)) {
_matchingAttribNames.push(attribName);
} else {
const remapped = CoreAttribute.remapName(mask);
if (attribName == remapped) {
_matchingAttribNames.push(attribName);
}
}
}
}
const uniqAttributeNames: string[] = [];
return arrayUniq(_matchingAttribNames, uniqAttributeNames);
}
}
export function markAttributeAsNeedsUpdateForFrame(
attribute: BufferAttribute | InstancedBufferAttribute | InterleavedBufferAttribute,
frame: number
) {
if (attribute instanceof BufferAttribute || attribute instanceof InstancedBufferAttribute) {
(attribute as BufferAttribute | InstancedBufferAttribute).version = frame;
} else {
if (attribute.data) {
attribute.data.version = frame;
}
}
}