@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
447 lines (427 loc) • 13.3 kB
text/typescript
import {
AttribValue,
ColorLike,
NumericAttribValue,
Vector2Like,
Vector3Like,
Vector4Like,
} from '../../../../types/GlobalTypes';
import {Vector4, Vector3, Vector2} from 'three';
import {Attribute, CoreAttribute} from '../../Attribute';
import {CoreEntity, CoreEntityWithObject} from '../../CoreEntity';
import {isArray} from '../../../Type';
import {BasePrimitiveAttribute} from './PrimitiveAttribute';
import {
DOT,
ComponentName,
COMPONENT_INDICES,
GroupString,
AttribClass,
AttribSize,
AttribType,
ComponentIndex,
} from '../../Constant';
import {PrimitiveAttributesDict} from './Common';
import {CoreObjectType, ObjectContent, ObjectBuilder} from '../../ObjectContent';
import {TypeAssert} from '../../../../engine/poly/Assert';
import {uniqRelatedEntityIds} from '../utils/Common';
import type {CoreVertex} from '../vertex/CoreVertex';
import type {CoreGroup} from '../../Group';
import type {PrimitiveGraph} from './PrimitiveGraph';
import {CorePoint} from '../point/CorePoint';
import {TraversedRelatedEntityData} from '../utils/TraversedRelatedEntities';
function _warnOverloadRequired(functionName: string) {
console.warn(`CorePrimitive.${functionName} needs to be overloaded`);
}
const _ids: number[] = [];
export abstract class CorePrimitive<T extends CoreObjectType> extends CoreEntityWithObject<T> {
builder<T extends CoreObjectType>(): ObjectBuilder<T> | undefined {
return undefined;
}
static entitiesCount<T extends CoreObjectType>(object: ObjectContent<T>) {
return 0;
}
static addAttribute<T extends CoreObjectType>(
object: ObjectContent<T>,
attribName: string,
attribute: BasePrimitiveAttribute
) {
_warnOverloadRequired('addAttribute');
}
static addNumericAttribute<T extends CoreObjectType>(
object: ObjectContent<T>,
attribName: string,
size: AttribSize = 1,
defaultValue: NumericAttribValue = 0
) {
_warnOverloadRequired('addNumericAttribute');
}
static attributes<T extends CoreObjectType>(object?: ObjectContent<T>): PrimitiveAttributesDict | undefined {
_warnOverloadRequired('attributes');
return;
}
attributes(): PrimitiveAttributesDict | undefined {
if (!this._object) {
return;
}
return (this.constructor as typeof CorePrimitive<T>).attributes(this._object);
}
static attribute<T extends CoreObjectType>(
object: ObjectContent<T>,
attribName: string
): BasePrimitiveAttribute | undefined {
const attributes = this.attributes(object);
if (!attributes) {
return;
}
return attributes[attribName];
}
attribute(attribName: string): BasePrimitiveAttribute | undefined {
if (!this._object) {
return;
}
return (this.constructor as typeof CorePrimitive<T>).attribute(this._object, attribName);
}
static renameAttribute<T extends CoreObjectType>(object: ObjectContent<T>, oldName: string, newName: string) {
const attributes = this.attributes(object);
if (!attributes) {
return;
}
const attribute = this.attribute(object, oldName);
if (!attribute) {
return;
}
attributes[newName] = attribute;
delete attributes[oldName];
}
static deleteAttribute<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string) {
const attributes = this.attributes(object);
if (!attributes) {
return;
}
delete attributes[attribName];
}
static attribSize<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string): number {
const attributes = this.attributes(object);
if (!attributes) {
return -1;
}
attribName = CoreAttribute.remapName(attribName);
return attributes[attribName].itemSize || 0;
}
attribSize(attribName: string): number {
if (!this._object) {
return 0;
}
return (this.constructor as typeof CorePrimitive<T>).attribSize(this._object, attribName);
}
static hasAttribute<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string): boolean {
const remappedName = CoreAttribute.remapName(attribName);
return this.attributes(object) ? this.attributes(object)![remappedName] != null : false;
}
hasAttribute(attribName: string): boolean {
if (!this._object) {
return false;
}
return (this.constructor as typeof CorePrimitive<T>).hasAttribute(this._object, attribName);
}
static attributeNames<T extends CoreObjectType>(object?: ObjectContent<T>): string[] {
const attributes = this.attributes(object);
if (!attributes) {
return [];
}
return Object.keys(attributes);
}
static attributeNamesMatchingMask<T extends CoreObjectType>(object: ObjectContent<T>, masksString: GroupString) {
return CoreAttribute.attribNamesMatchingMask(masksString, this.attributeNames(object));
}
static attribValue<T extends CoreObjectType>(
object: ObjectContent<T>,
index: number,
attribName: string,
target?: Vector2 | Vector3 | Vector4
): AttribValue {
if (attribName === Attribute.PRIMITIVE_INDEX) {
return index;
}
let componentName: ComponentName | null = null;
let componentIndex: ComponentIndex | null = null;
if (attribName[attribName.length - 2] === DOT) {
componentName = attribName[attribName.length - 1] as ComponentName;
componentIndex = COMPONENT_INDICES[componentName];
attribName = attribName.substring(0, attribName.length - 2);
}
const remapedName = CoreAttribute.remapName(attribName);
if (remapedName == Attribute.POSITION) {
return this.position(object, index, target as Vector3);
}
if (remapedName == Attribute.NORMAL) {
return this.normal(object, index, target as Vector3);
}
const attrib = this.attribute(object, remapedName);
if (attrib) {
const {array} = attrib;
const itemSize = attrib.itemSize;
const startIndex = index * itemSize;
if (componentIndex == null) {
switch (itemSize) {
case 1:
return array[startIndex];
break;
case 2:
target = target || new Vector2();
target.fromArray(array as number[], startIndex);
return target;
break;
case 3:
target = target || new Vector3();
target.fromArray(array as number[], startIndex);
return target;
break;
case 4:
target = target || new Vector4();
target.fromArray(array as number[], startIndex);
return target;
break;
default:
throw `size not valid (${itemSize})`;
}
} else {
switch (itemSize) {
case 1:
return array[startIndex];
break;
default:
return array[startIndex + componentIndex];
}
}
// }
} else {
const attributesDict = this.attributes(object) || {};
const attribNames: string[] = Object.keys(attributesDict);
const message = `attrib ${attribName} not found. availables are: ${attribNames.join(',')}`;
console.warn(message);
throw message;
}
}
attribValue(attribName: string, target?: Vector2 | Vector3 | Vector4): AttribValue {
if (!this._object) {
return 0;
}
return (this.constructor as typeof CorePrimitive<T>).attribValue(this._object, this._index, attribName, target);
}
attribValueNumber(attribName: string) {
const attrib = this.attribute(attribName);
if (!attrib) {
return 0;
}
return attrib.array[this._index];
}
attribValueVector2(attribName: string, target: Vector2) {
const attrib = this.attribute(attribName);
if (!attrib) {
return;
}
target.fromArray(attrib.array as number[], this._index * 2);
return target;
}
attribValueVector3(attribName: string, target: Vector3) {
const attrib = this.attribute(attribName);
if (!attrib) {
return;
}
target.fromArray(attrib.array as number[], this._index * 3);
return target;
}
attribValueVector4(attribName: string, target: Vector4) {
const attrib = this.attribute(attribName);
if (!attrib) {
return;
}
target.fromArray(attrib.array as number[], this._index * 4);
return target;
}
static attribType<T extends CoreObjectType>(object: ObjectContent<T> | undefined, attribName: string): AttribType {
const attribute = object ? this.attribute(object, attribName) : null;
if (attribute && attribute?.isString == true) {
return AttribType.STRING;
} else {
return AttribType.NUMERIC;
}
}
attribType(attribName: string): AttribType {
return (this.constructor as typeof CorePrimitive).attribType(this._object, attribName);
}
static stringAttribValue<T extends CoreObjectType>(object: ObjectContent<T>, index: number, attribName: string) {
return this.attribValue(object, index, attribName);
}
stringAttribValue(attribName: string) {
return this.attribValue(attribName) as string;
}
// setPosition(newPosition: Vector3) {
// this.setAttribValueFromVector3(Attribute.POSITION, newPosition);
// }
// setNormal(newNormal: Vector3) {
// return this.setAttribValueFromVector3(Attribute.NORMAL, newNormal);
// }
static position<T extends CoreObjectType>(
object: ObjectContent<T> | undefined,
primitiveIndex: number,
target: Vector3
): Vector3 {
_warnOverloadRequired('position');
return target;
}
static normal<T extends CoreObjectType>(
object: ObjectContent<T> | undefined,
primitiveIndex: number,
target: Vector3
): Vector3 {
_warnOverloadRequired('normal');
return target;
}
static computeVertexNormalsIfAttributeVersionChanged<T extends CoreObjectType>(object: ObjectContent<T>) {
_warnOverloadRequired('computeVertexNormalsIfAttributeVersionChanged');
}
setAttribValue(attribName: string, value: NumericAttribValue | string) {
const attrib = this.attribute(attribName);
if (!attrib) {
console.warn(`no attribute ${attribName}`);
return;
}
const array = attrib.array;
const attribSize = attrib.itemSize;
if (isArray(value)) {
for (let i = 0; i < attribSize; i++) {
array[this._index * attribSize + i] = value[i];
}
return;
}
switch (attribSize) {
case 1:
array[this._index] = value as number | string;
break;
case 2:
const v2 = value as Vector2Like;
const i2 = this._index * 2;
array[i2 + 0] = v2.x;
array[i2 + 1] = v2.y;
break;
case 3:
const isColor = (value as ColorLike).r != null;
const i3 = this._index * 3;
if (isColor) {
const col = value as ColorLike;
array[i3 + 0] = col.r;
array[i3 + 1] = col.g;
array[i3 + 2] = col.b;
} else {
const v3 = value as Vector3Like;
array[i3 + 0] = v3.x;
array[i3 + 1] = v3.y;
array[i3 + 2] = v3.z;
}
break;
case 4:
const v4 = value as Vector4Like;
const i4 = this._index * 4;
array[i4 + 0] = v4.x;
array[i4 + 1] = v4.y;
array[i4 + 2] = v4.z;
array[i4 + 3] = v4.w;
break;
default:
console.warn(`CorePrimitive.setAttribValue does not yet allow attribSize ${attribSize}`);
throw `attrib size ${attribSize} not implemented`;
}
}
setAttribValueFromNumber(attribName: string, value: number) {
const attrib = this.attribute(attribName);
if (!attrib) {
return;
}
const array = attrib.array as number[];
array[this._index] = value;
}
setAttribValueFromVector2(attribName: string, value: Vector2) {
const attrib = this.attribute(attribName);
if (!attrib || attrib.isString == true) {
return;
}
value.toArray(attrib.array as number[], this._index * 2);
}
setAttribValueFromVector3(attribName: string, value: Vector3) {
const attrib = this.attribute(attribName);
if (!attrib || attrib.isString == true) {
return;
}
value.toArray(attrib.array as number[], this._index * 3);
}
setAttribValueFromVector4(attribName: string, value: Vector4) {
const attrib = this.attribute(attribName);
if (!attrib || attrib.isString == true) {
return;
}
value.toArray(attrib.array as number[], this._index * 4);
}
//
//
// RELATED ENTITIES
//
//
static override relatedPointIds<T extends CoreObjectType>(
object: ObjectContent<T>,
pointIndex: number,
target: number[],
traversedRelatedEntityData?: TraversedRelatedEntityData
): void {
const ids = traversedRelatedEntityData ? traversedRelatedEntityData[AttribClass.VERTEX].ids : _ids;
this.relatedVertexIds(object, pointIndex, ids);
uniqRelatedEntityIds(
ids,
(vertexId, relatedEntityIds) => {
this.relatedVertexClass(object).relatedPointIds(object, vertexId, relatedEntityIds);
},
target
);
}
static override relatedPointClass<T extends CoreObjectType>(object: ObjectContent<T>) {
return this.relatedVertexClass(object).relatedPointClass(object);
}
relatedEntities(
attribClass: AttribClass,
coreGroup: CoreGroup,
target: CoreEntity[],
traversedRelatedEntityData?: TraversedRelatedEntityData
): void {
switch (attribClass) {
case AttribClass.POINT: {
this.relatedPoints(target as CorePoint<T>[], traversedRelatedEntityData);
return;
}
case AttribClass.VERTEX: {
this.relatedVertices(target as CoreVertex<T>[], traversedRelatedEntityData);
return;
}
case AttribClass.PRIMITIVE: {
target.length = 1;
target[0] = this;
return;
}
case AttribClass.OBJECT: {
this.relatedObjects(target as CoreEntityWithObject<T>[], traversedRelatedEntityData);
return;
}
case AttribClass.CORE_GROUP: {
target.length = 1;
target[0] = coreGroup;
return;
}
}
TypeAssert.unreachable(attribClass);
}
static graph(object: ObjectContent<CoreObjectType>): PrimitiveGraph | undefined {
console.warn('CorePrimitive.graph needs to be overriden');
return undefined;
}
}