UNPKG

@polygonjs/polygonjs

Version:

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

411 lines (402 loc) 11.6 kB
import type {Ref} from '@vue/reactivity'; import {Intersection, Material, Object3D} from 'three'; import {ConvertToStrings} from '../../../../../types/GlobalTypes'; import {MouseButton, MouseButtons} from '../../../../../core/MouseButton'; import {TypeAssert} from '../../../../poly/Assert'; export interface PriorityOptions { blockObjectsBehind: boolean; skipIfObjectsInFront: boolean; } export interface CPUOptions { traverseChildren: boolean; pointsThreshold: number; lineThreshold: number; intersectionRef: Ref<Intersection | null>; } export interface GPUOptions { // if a worldPosMaterial is given, we use it and only need a single render call. // if not, we render need 2 renders, 1 to write to the depth buffer and another to read it (in addition to readRenderTargetPixels) worldPosMaterial: Material | null; distanceRef: Ref<number>; } export interface ObjectOptions { priority: PriorityOptions; cpu?: CPUOptions; gpu?: GPUOptions; } export interface EmptyOptions {} export function hasCPUOptions(optionsList: ObjectOptions[]): boolean { for (const options of optionsList) { if (options.cpu != null) { return true; } } return false; } export function hasGPUOptions(optionsList: ObjectOptions[]): boolean { for (const options of optionsList) { if (options.gpu != null) { return true; } } return false; } export function GPUOptionsDepthBufferRequired(optionsList: ObjectOptions[]): boolean { for (const options of optionsList) { if (options.gpu != null && options.gpu.worldPosMaterial == null) { return true; } } return false; } function _cpuOptionsEqual(options1: CPUOptions, options2: CPUOptions): boolean { return ( options1.traverseChildren == options2.traverseChildren && options1.pointsThreshold == options2.pointsThreshold && options1.lineThreshold == options2.lineThreshold ); } export function CPUOptionsEqual(optionsList: ObjectOptions[]): boolean { let firstCPUOptions: CPUOptions | undefined; for (const options of optionsList) { if (options.cpu != null) { if (firstCPUOptions == null) { firstCPUOptions = options.cpu; } else { if (!_cpuOptionsEqual(firstCPUOptions, options.cpu)) { return false; } } } } return true; } export function CPUOptionsMax(optionsList: ObjectOptions[], target: CPUOptions): CPUOptions { target.traverseChildren = false; target.pointsThreshold = -1; target.lineThreshold = -1; for (const options of optionsList) { if (options.cpu != null) { if (target.traverseChildren == false && options.cpu.traverseChildren == true) { target.traverseChildren = options.cpu.traverseChildren; } if (target.pointsThreshold < options.cpu.pointsThreshold) { target.pointsThreshold = options.cpu.pointsThreshold; } if (target.lineThreshold < options.cpu.lineThreshold) { target.lineThreshold = options.cpu.lineThreshold; } } } return target; } export enum Status { REQUIRED = 0, //'required', OPTIONAL = 1, //'optional', FORBIDDEN = 2, //'forbidden', } export const STATUS_OPTIONS: Status[] = [Status.REQUIRED, Status.OPTIONAL, Status.FORBIDDEN]; export const STATUS_OPTION_LABEL: string[] = ['required', 'optional', 'forbidden']; export const DEFAULT_STATUS_OPTION = STATUS_OPTIONS.indexOf(Status.OPTIONAL); export const STATUS_MENU_OPTIONS = { menu: { entries: STATUS_OPTIONS.map((value) => ({ value, name: STATUS_OPTION_LABEL[value], })), }, }; export interface ButtonOptions { left: boolean; middle: boolean; right: boolean; } export interface ButtonsOptions { left: Status; middle: Status; right: Status; } export interface ModifierOptions { ctrl: Status; shift: Status; alt: Status; } // export interface ModifierIndexOptions { // ctrl: number; // shift: number; // alt: number; // } // export function modifierIndexToModifierOptions(options: ModifierIndexOptions): ModifierOptions { // return { // ctrl: POINTER_EVENT_MODIFIER_OPTIONS[options.ctrl], // shift: POINTER_EVENT_MODIFIER_OPTIONS[options.shift], // alt: POINTER_EVENT_MODIFIER_OPTIONS[options.alt], // }; // } export interface ButtonAndModifierOptions { button: ButtonOptions; modifier: ModifierOptions; } export interface ButtonsAndModifierOptions { button: ButtonsOptions; modifier: ModifierOptions; } export interface OnlyModifierOptions { modifier: ModifierOptions; } // export interface ButtonAndModifierIndexOptions { // button: ButtonOptions; // modifier: ModifierIndexOptions; // } export interface ButtonAndModifierOptionsAsString { button: ConvertToStrings<ButtonOptions>; modifier: ConvertToStrings<ModifierOptions>; } export interface ButtonsAndModifierOptionsAsString { button: ConvertToStrings<ButtonsOptions>; modifier: ConvertToStrings<ModifierOptions>; } export interface ModifierOptionsAsString { modifier: ConvertToStrings<ModifierOptions>; } interface PropertyWithButtonConfig { config: ButtonAndModifierOptions; } interface PropertyWithButtonsConfig { config: ButtonsAndModifierOptions; } interface PropertyWithModifierConfig { config: OnlyModifierOptions; } export interface ButtonConfig { button: MouseButton; ctrl: boolean; shift: boolean; alt: boolean; } export interface ButtonsConfig { buttons: MouseButtons; ctrl: boolean; shift: boolean; alt: boolean; } function statusMatch(modifierProperty: Status, value: boolean): boolean { switch (modifierProperty) { case Status.REQUIRED: { return value == true; } case Status.OPTIONAL: { return true; } case Status.FORBIDDEN: { return value == false; } } TypeAssert.unreachable(modifierProperty); } export function propertyMatchesModifiersConfig( propertyConfig: OnlyModifierOptions, buttonsConfig: ButtonsConfig | ButtonConfig ) { return ( statusMatch(propertyConfig.modifier.ctrl, buttonsConfig.ctrl) && statusMatch(propertyConfig.modifier.shift, buttonsConfig.shift) && statusMatch(propertyConfig.modifier.alt, buttonsConfig.alt) ); } export function propertyMatchesButtonConfig(propertyConfig: ButtonAndModifierOptions, buttonConfig: ButtonConfig) { switch (buttonConfig.button) { case MouseButton.LEFT: { if (propertyConfig.button.left == false) { return false; } break; } case MouseButton.MIDDLE: { if (propertyConfig.button.middle == false) { return false; } break; } case MouseButton.RIGHT: { if (propertyConfig.button.right == false) { return false; } break; } } return propertyMatchesModifiersConfig(propertyConfig, buttonConfig); } export function propertyMatchesButtonsConfig(propertyConfig: ButtonsAndModifierOptions, buttonsConfig: ButtonsConfig) { switch (buttonsConfig.buttons) { case MouseButtons.LEFT: { if ( propertyConfig.button.left == Status.FORBIDDEN || propertyConfig.button.middle == Status.REQUIRED || propertyConfig.button.right == Status.REQUIRED ) { return false; } break; } case MouseButtons.MIDDLE: { if ( propertyConfig.button.left == Status.REQUIRED || propertyConfig.button.middle == Status.FORBIDDEN || propertyConfig.button.right == Status.REQUIRED ) { return false; } break; } case MouseButtons.RIGHT: { if ( propertyConfig.button.left == Status.REQUIRED || propertyConfig.button.middle == Status.REQUIRED || propertyConfig.button.right == Status.FORBIDDEN ) { return false; } break; } case MouseButtons.LEFT_RIGHT: { if ( propertyConfig.button.left == Status.FORBIDDEN || propertyConfig.button.middle == Status.REQUIRED || propertyConfig.button.right == Status.FORBIDDEN ) { return false; } break; } case MouseButtons.LEFT_MIDDLE: { if ( propertyConfig.button.left == Status.FORBIDDEN || propertyConfig.button.middle == Status.FORBIDDEN || propertyConfig.button.right == Status.REQUIRED ) { return false; } break; } case MouseButtons.MIDDLE_RIGHT: { if ( propertyConfig.button.left == Status.REQUIRED || propertyConfig.button.middle == Status.FORBIDDEN || propertyConfig.button.right == Status.FORBIDDEN ) { return false; } break; } case MouseButtons.LEFT_MIDDLE_RIGHT: { if ( propertyConfig.button.left == Status.FORBIDDEN || propertyConfig.button.middle == Status.FORBIDDEN || propertyConfig.button.right == Status.FORBIDDEN ) { return false; } break; } } return propertyMatchesModifiersConfig(propertyConfig, buttonsConfig); } function propertiesMatchesButtonConfig( propertiesList: PropertyWithButtonConfig[], buttonConfig: ButtonConfig ): boolean { for (const properties of propertiesList) { if (propertyMatchesButtonConfig(properties.config, buttonConfig)) { return true; } } return false; } function propertiesMatchesButtonsConfig( propertiesList: PropertyWithButtonsConfig[], buttonConfig: ButtonsConfig ): boolean { for (const properties of propertiesList) { if (propertyMatchesButtonsConfig(properties.config, buttonConfig)) { return true; } } return false; } function propertiesMatchesModifiersConfig( propertiesList: PropertyWithModifierConfig[], buttonConfig: ButtonsConfig ): boolean { for (const properties of propertiesList) { if (propertyMatchesModifiersConfig(properties.config, buttonConfig)) { return true; } } return false; } export function buttonConfigFromEvent(event: Readonly<PointerEvent | MouseEvent | TouchEvent>, target: ButtonConfig) { target.button = (event as PointerEvent).button || MouseButton.LEFT; target.ctrl = event.ctrlKey; target.shift = event.shiftKey; target.alt = event.altKey; } export function buttonsConfigFromEvent(event: Readonly<PointerEvent | MouseEvent | TouchEvent>, target: ButtonsConfig) { target.buttons = (event as MouseEvent).buttons || MouseButtons.LEFT; target.ctrl = event.ctrlKey; target.shift = event.shiftKey; target.alt = event.altKey; } const _buttonConfig: ButtonConfig = {button: MouseButton.LEFT, ctrl: false, shift: false, alt: false}; const _buttonsConfig: ButtonsConfig = {buttons: MouseButtons.LEFT, ctrl: false, shift: false, alt: false}; export function filterObjectsWithMatchButtonConfig( event: Readonly<PointerEvent | MouseEvent | TouchEvent>, objects: Object3D[], propertiesListByObject: Map<Object3D, PropertyWithButtonConfig[]>, target: Object3D[] ) { target.length = 0; buttonConfigFromEvent(event, _buttonConfig); for (const object of objects) { const propertiesList = propertiesListByObject.get(object); if (propertiesList) { if (propertiesMatchesButtonConfig(propertiesList, _buttonConfig)) { target.push(object); } } } } export function filterObjectsWithMatchButtonsConfig( event: Readonly<PointerEvent | MouseEvent | TouchEvent>, objects: Object3D[], propertiesListByObject: Map<Object3D, PropertyWithButtonsConfig[]>, target: Object3D[] ) { target.length = 0; buttonsConfigFromEvent(event, _buttonsConfig); for (const object of objects) { const propertiesList = propertiesListByObject.get(object); if (propertiesList) { if (propertiesMatchesButtonsConfig(propertiesList, _buttonsConfig)) { target.push(object); } } } } export function filterObjectsWithMatchModifiersConfig( event: Readonly<PointerEvent | MouseEvent | TouchEvent>, objects: Object3D[], propertiesListByObject: Map<Object3D, PropertyWithModifierConfig[]>, target: Object3D[] ) { target.length = 0; buttonsConfigFromEvent(event, _buttonsConfig); for (const object of objects) { const propertiesList = propertiesListByObject.get(object); if (propertiesList) { if (propertiesMatchesModifiersConfig(propertiesList, _buttonsConfig)) { target.push(object); } } } }