Skip to content

Commit

Permalink
Split custom props (preparation for #146)
Browse files Browse the repository at this point in the history
  • Loading branch information
brianzinn committed Aug 3, 2021
1 parent f4b85ae commit 77b3c5d
Show file tree
Hide file tree
Showing 21 changed files with 332 additions and 268 deletions.
65 changes: 1 addition & 64 deletions src/CreatedInstance.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Observer } from "@babylonjs/core/Misc/observable"
import { Nullable } from "@babylonjs/core/types";
import { CustomProps } from "./CustomProps";
import { LifecycleListener } from "./LifecycleListener"
import { HasPropsHandlers } from "./PropsHandler"

Expand All @@ -24,70 +25,6 @@ export interface InstanceMetadataParameter {

}

/**
* Props passed from controls that are not part of generated props and we are handling ourselves.
*
* TODO: move props that only apply to specific objects to be only for those ones (ie: defaultKeyboard, shadowCasters, etc.)
*/
export type CustomProps = {
/**
* Only applicable for AdvanceDynamicTexture to attach to a mesh. ADT.CreateForMesh(parent, ...) (TODO: add 'ByName')
*/
createForParentMesh?: boolean
/**
* for 3D control ".content" (which is 2D)
*/
childrenAsContent?: boolean
/**
* for VirtualKeyboard (2d input control names)
*/
connectControlNames?: string[]
/**
* for VirtualKeyboard
*/
defaultKeyboard?: boolean

/**
* for Control3D, which has position, but not other properties like rotation.
*/
linkToTransformNodeByName?: string
/**
* List of mesh names to search for, which will be added as shadow casters.
*/
shadowCasters?: string[]
/**
* List of mesh names to exclude from casting shadows (all other meshes by name will cast shadows)
*/
shadowCastersExcluding?: string[]
/**
* For attaching the same material multiple meshes (by mesh name)
*/
attachToMeshesByName?: string[]
onControlAdded?: (instance: CreatedInstance<any>) => void
/**
* Assign to this property on the parent. Parent property is cleared on umnount.
*/
assignTo?: string | string[]
/**
* Assigned from this existing property on the parent. Will assign this host element to a parent property that contains an existing instance (no new instances created and no dispose called).
*/
assignFrom?: string
/**
* for VRExperienceHelper
*/
enableInteractions?: boolean
/**
* allows components to use instances of objects created non-declaratively
*/
fromInstance?: any
/**
* To automatically dispose of the underlying object when "fromInstance" is used. Is not applied for regularly instanced objects.
*
* Default: false
*/
disposeInstanceOnUnmount?: boolean
}

export interface CreatedInstanceMetadata extends InstanceMetadataParameter {
className: string
}
Expand Down
80 changes: 80 additions & 0 deletions src/CustomProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { CreatedInstance } from "./CreatedInstance";

/**
* Props passed from controls that are not part of generated props and custom handling. Typically used to aid declarative composition.
*/
export type CustomProps = {
/**
* Assign to this property on the parent. Parent property is cleared on umnount.
*/
assignTo?: string | string[]
/**
* Assigned from this existing property on the parent. Will assign this host element to a parent property that contains an existing instance (no new instances created and no dispose called).
*/
assignFrom?: string
/**
* allows components to use instances of objects created non-declaratively
*/
fromInstance?: any
/**
* To automatically dispose of the underlying object when "fromInstance" is used. Is not applied for regularly instanced objects.
*
* Default: false
*/
disposeInstanceOnUnmount?: boolean
}

export type MaterialCustomProps = {
/**
* For attaching the same material to multiple meshes (by mesh name)
*/
attachToMeshesByName?: string[]
} & CustomProps;

export type ShadowGeneratorCustomProps = {
/**
* List of mesh names to search for, which will be added as shadow casters.
*/
shadowCasters?: string[]
/**
* List of mesh names to exclude from casting shadows (all other meshes by name will cast shadows)
*/
shadowCastersExcluding?: string[]
} & CustomProps;

export type VirtualKeyboardCustomProps = {
/**
* for VirtualKeyboard (2d input control names)
*/
connectControlNames?: string[]
/**
* for VirtualKeyboard
*/
defaultKeyboard?: boolean
} & CustomProps;

export type VRExperienceHelperCustomProps = {
enableInteractions?: boolean
} & CustomProps;

export type Control3DCustomProps = {
/**
* for 3D control ".content" (which is 2D)
*/
childrenAsContent?: boolean
/**
* for Control3D, which has position, but not other properties like rotation.
*/
linkToTransformNodeByName?: string
/**
* See manager and Control lifecycle listener for details.
*/
onControlAdded?: (instance: CreatedInstance<any>) => void
} & CustomProps;

export type ADTCustomProps = {
/**
* Only applicable for AdvanceDynamicTexture to attach to a mesh. ADT.CreateForMesh(parent, ...)
*/
createForParentMesh?: boolean
};
12 changes: 7 additions & 5 deletions src/ReactBabylonJSHostConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import * as BABYLONEXT from './extensions';
import * as GENERATED from './generatedCode';
import * as CUSTOM_HOSTS from './customHosts';

import { CreatedInstance, CreatedInstanceMetadata, CustomProps } from './CreatedInstance';
import { CreatedInstance, CreatedInstanceMetadata} from './CreatedInstance';
import { ADTCustomProps, Control3DCustomProps, CustomProps, MaterialCustomProps, ShadowGeneratorCustomProps, VirtualKeyboardCustomProps } from './CustomProps';
import { HasPropsHandlers, PropertyUpdate, UpdatePayload, PropsHandler, CustomPropsHandler, PropChangeType } from './PropsHandler';
import { LifecycleListener } from "./LifecycleListener";
import { GeneratedParameter, CreationType } from './codeGenerationDescriptors';
Expand Down Expand Up @@ -332,10 +333,11 @@ const ReactBabylonJSHostConfig: HostConfig<
let metadata: CreatedInstanceMetadata;
let babylonObject: any | undefined = undefined

// TODO: Add these to just the 'type' of component they apply to.
let customProps: CustomProps = {
let customProps: CustomProps & (Control3DCustomProps | ADTCustomProps | VirtualKeyboardCustomProps | ShadowGeneratorCustomProps | MaterialCustomProps) = {
// Control3D
childrenAsContent: props.childrenAsContent === true, // ie: Button3D.container instead of .addControl()
createForParentMesh: props.createForParentMesh === true, // AdvancedDynamicTexture attached to parent mesh (TODO: add forMeshByName="")
// AdvancedDynamicTexture
createForParentMesh: props.createForParentMesh === true,
onControlAdded: typeof props.onControlAdded === "function" ? props.onControlAdded : undefined,
connectControlNames: props.connectControlNames, // VirtualKeyboard to connect inputs by name.
defaultKeyboard: props.defaultKeyboard === true,
Expand All @@ -346,7 +348,7 @@ const ReactBabylonJSHostConfig: HostConfig<
assignTo: props.assignTo, // here a lifecycle listener can dynamically attach to another property (ie: Mesh to DynamicTerrain -> 'mesh.material')
assignFrom: props.assignFrom,
disposeInstanceOnUnmount: props.assignFrom === undefined
}
};

if (customProps.assignFrom !== undefined) {
// will be assigned once parented in lifecyclelistener
Expand Down
23 changes: 12 additions & 11 deletions src/customHosts/AdvancedDynamicTextureLifecycleListener.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { Color3, StandardMaterial, Mesh } from '@babylonjs/core'
import { Color3, StandardMaterial, Mesh } from '@babylonjs/core';
import { AdvancedDynamicTexture } from '@babylonjs/gui/2D/advancedDynamicTexture';

import BaseLifecycleListener from './BaseLifecycleListener';
import { CreatedInstance } from '../CreatedInstance'
import { FiberAdvancedDynamicTextureProps } from '../generatedProps'
import { ADTCustomProps, VirtualKeyboardCustomProps } from '../CustomProps';
import { CreatedInstance } from '../CreatedInstance';
import { FiberAdvancedDynamicTextureProps } from '../generatedProps';

export default class AdvancedDynamicTextureLifecycleListener extends BaseLifecycleListener<AdvancedDynamicTexture, FiberAdvancedDynamicTextureProps> {

onMount(instance: CreatedInstance<AdvancedDynamicTexture>): void {
instance.state = {added: true}; // allow children to attach
instance.state = { added: true }; // allow children to attach
this.addControls(instance);

if (instance.customProps.createForParentMesh) {
if ((instance.customProps as ADTCustomProps).createForParentMesh) {
// console.log('for parent mesh', instance.parent ? instance.parent.babylonJsObject : 'error: no parent object')

let mesh: Mesh = instance.parent!.hostInstance; // should crawl parent hierarchy for a mesh
Expand All @@ -22,7 +23,7 @@ export default class AdvancedDynamicTextureLifecycleListener extends BaseLifecyc
material.diffuseColor = Color3.Black();
material.specularColor = Color3.Black();

if(instance.hostInstance === undefined) {
if (instance.hostInstance === undefined) {
console.error('missing instance');
} else {
if (this.props.hasAlpha) {
Expand All @@ -39,7 +40,7 @@ export default class AdvancedDynamicTextureLifecycleListener extends BaseLifecyc

// set to true unless explicitly not wanted.
// connects the texture to a hosting mesh to enable interactions
let supportPointerMove = (this.props as any).supportPointerMove !== false ? true : false
let supportPointerMove = (this.props as any).supportPointerMove !== false ? true : false;

instance.hostInstance!.attachToMesh(mesh, supportPointerMove);
}
Expand All @@ -55,8 +56,8 @@ export default class AdvancedDynamicTextureLifecycleListener extends BaseLifecyc
}
})

if (instance.customProps.connectControlNames !== undefined && Array.isArray(instance.customProps.connectControlNames)) {
let controlNames: string[] = instance.customProps.connectControlNames;
if ((instance.customProps as VirtualKeyboardCustomProps).connectControlNames !== undefined && Array.isArray((instance.customProps as VirtualKeyboardCustomProps).connectControlNames)) {
let controlNames: string[] = (instance.customProps as VirtualKeyboardCustomProps).connectControlNames!;
let root = instance;
while (root.parent !== null) {
root = root.parent;
Expand All @@ -79,11 +80,11 @@ export default class AdvancedDynamicTextureLifecycleListener extends BaseLifecyc
keyboard.hostInstance.connect(searchInstance.hostInstance);
}

searchInstance.children.forEach(child => this.connect(keyboard,child,controlNames));
searchInstance.children.forEach(child => this.connect(keyboard, child, controlNames));
}
}

/**
* This is attached by convention in react-reconciler HostConfig.
*/
export class ADTFullscreenUILifecycleListener extends AdvancedDynamicTextureLifecycleListener {/* empty */}
export class ADTFullscreenUILifecycleListener extends AdvancedDynamicTextureLifecycleListener {/* empty */ }
31 changes: 16 additions & 15 deletions src/customHosts/BaseShadowGeneratorLifecycleListener.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { Scene, AbstractMesh, Observer, Nullable, DirectionalLight, ShadowGenerator } from '@babylonjs/core';

import { CreatedInstance } from '../CreatedInstance';
import { ShadowGeneratorCustomProps } from '../CustomProps';
import DeferredCreationLifecycleListener from './DeferredCreationLifecycleListener';

/**
* Create a Shadow Generator (CascadedShadowGenerator extends ShadowGenerator, so add/remove shadow casters is from parent class)
*/
export default abstract class BaseShadowGeneratorLifecycleListener<T extends ShadowGenerator, U> extends DeferredCreationLifecycleListener<T, U> {

private onMeshAddedObservable: Nullable<Observer<AbstractMesh>> = null;
private onMeshRemovedObservable: Nullable<Observer<AbstractMesh>> = null;

abstract createShadowGenerator: (mapSize: number, light: DirectionalLight, useFullFloatFirst?: boolean) => T;

abstract get generatorType(): string;

createInstance = (instance: CreatedInstance<T>, scene: Scene, props: any) : Nullable<T> => {
createInstance = (instance: CreatedInstance<T>, scene: Scene, props: any): Nullable<T> => {
let tmp: CreatedInstance<any> | null = instance.parent;
let result: Nullable<T> = null;

Expand All @@ -33,20 +34,20 @@ export default abstract class BaseShadowGeneratorLifecycleListener<T extends Sha
return null;
}

if (instance.customProps.shadowCasters) {
if (!Array.isArray(instance.customProps.shadowCasters)) {
console.error('Shadow casters must be an array (of strings).', instance.customProps.shadowCasters);
if ((instance.customProps as ShadowGeneratorCustomProps).shadowCasters) {
if (!Array.isArray((instance.customProps as ShadowGeneratorCustomProps).shadowCasters)) {
console.error('Shadow casters must be an array (of strings).', (instance.customProps as ShadowGeneratorCustomProps).shadowCasters);
return null;
}

let shadowCasters: string[] = instance.customProps.shadowCasters;
let shadowCasters: string[] = (instance.customProps as ShadowGeneratorCustomProps).shadowCasters!;

// TODO: also need a listener for models or if we want to add a predicate:
this.onMeshAddedObservable = scene.onNewMeshAddedObservable.add((mesh: AbstractMesh) => {
if (shadowCasters.indexOf(mesh.name) >= 0) {
instance.hostInstance!.addShadowCaster(mesh);
}
})
});

this.onMeshRemovedObservable = scene.onMeshRemovedObservable.add((mesh: AbstractMesh) => {
if (shadowCasters.indexOf(mesh.name) >= 0) {
Expand All @@ -58,20 +59,20 @@ export default abstract class BaseShadowGeneratorLifecycleListener<T extends Sha
if (shadowCasters.indexOf(mesh.name) >= 0) {
instance.hostInstance!.addShadowCaster(mesh);
}
})
} else if (instance.customProps.shadowCastersExcluding) {
if (!Array.isArray(instance.customProps.shadowCastersExcluding)) {
console.error('Shadow casters excluding must be an array (of strings).', instance.customProps.shadowCastersExcluding);
});
} else if ((instance.customProps as ShadowGeneratorCustomProps).shadowCastersExcluding) {
if (!Array.isArray((instance.customProps as ShadowGeneratorCustomProps).shadowCastersExcluding)) {
console.error('Shadow casters excluding must be an array (of strings).', (instance.customProps as ShadowGeneratorCustomProps).shadowCastersExcluding);
} else {
let shadowCastersExcluding: string[] = instance.customProps.shadowCastersExcluding;
let shadowCastersExcluding: string[] = (instance.customProps as ShadowGeneratorCustomProps).shadowCastersExcluding!;

// TODO: also need a listener for models or if we want to add a predicate:
this.onMeshAddedObservable = scene.onNewMeshAddedObservable.add((mesh: AbstractMesh) => {
if (shadowCastersExcluding.indexOf(mesh.name) === -1) {
instance.hostInstance!.addShadowCaster(mesh);
}
})
});

this.onMeshRemovedObservable = scene.onMeshRemovedObservable.add((mesh: AbstractMesh) => {
if (shadowCastersExcluding.indexOf(mesh.name) === -1) {
instance.hostInstance!.removeShadowCaster(mesh);
Expand All @@ -82,7 +83,7 @@ export default abstract class BaseShadowGeneratorLifecycleListener<T extends Sha
if (shadowCastersExcluding.indexOf(mesh.name) === -1) {
instance.hostInstance!.addShadowCaster(mesh);
}
})
});
}
}

Expand Down
11 changes: 2 additions & 9 deletions src/customHosts/GUI2DControlLifecycleListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { VirtualKeyboard } from '@babylonjs/gui/2D/controls/virtualKeyboard';

import BaseLifecycleListener from './BaseLifecycleListener';
import { CreatedInstance } from '../CreatedInstance';
import { VirtualKeyboardCustomProps } from '../CustomProps';

export default class GUI2DControlLifecycleListener extends BaseLifecycleListener<Control, any> {

Expand All @@ -12,7 +13,7 @@ export default class GUI2DControlLifecycleListener extends BaseLifecycleListener
return;
}

if (instance.customProps.defaultKeyboard === true && instance.hostInstance instanceof VirtualKeyboard) {
if ((instance.customProps as VirtualKeyboardCustomProps).defaultKeyboard === true && instance.hostInstance instanceof VirtualKeyboard) {
// TODO: Generate from factory method. VirtualKeyboard.CreateDefaultLayout()
instance.hostInstance.addKeysRow(['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '\u2190']);
instance.hostInstance.addKeysRow(['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']);
Expand Down Expand Up @@ -57,14 +58,6 @@ export default class GUI2DControlLifecycleListener extends BaseLifecycleListener
}
})

if (instance.customProps.connectControlNames !== undefined && Array.isArray(instance.customProps.connectControlNames)) {
// let controlNames: string[] = instance.customProps.connectControlNames
let root = instance;
while (root.parent !== null) {
root = root.parent;
}
}

instance.children.forEach(child => {
this.addControls(child);
})
Expand Down
Loading

0 comments on commit 77b3c5d

Please sign in to comment.