Skip to content

Commit

Permalink
#146 Add initial support for GlowLayer to include all child meshes in…
Browse files Browse the repository at this point in the history
… inclusion list. Works for regular meshes, but not for models yet.
  • Loading branch information
brianzinn committed Aug 5, 2021
1 parent 77b3c5d commit f5c461b
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 21 deletions.
5 changes: 3 additions & 2 deletions src/CreatedInstance.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Observer } from "@babylonjs/core/Misc/observable"
import { Nullable } from "@babylonjs/core/types";
import { CustomProps } from "./CustomProps";
import { AnyCustomProps, CustomProps } from "./CustomProps";
import { LifecycleListener } from "./LifecycleListener"
import { HasPropsHandlers } from "./PropsHandler"

Expand All @@ -21,6 +21,7 @@ export interface InstanceMetadataParameter {
customType?: boolean // not used by code-gen
isCamera?: boolean
isEffectLayer?: boolean;
isGlowLayer?: boolean;
isBehavior?: boolean;

}
Expand All @@ -44,7 +45,7 @@ export interface CreatedInstance<T> {
parent: CreatedInstance<any> | null // Not the same as parent in BabylonJS, this is for internal reconciler structure. ie: graph walking
children: CreatedInstance<any>[]
state?: any
customProps: CustomProps
customProps: AnyCustomProps
// TODO: Consider merging these last 2 into a single class/container.
propsHandlers?: HasPropsHandlers<T> // These are mostly generated
lifecycleListener?: LifecycleListener<T> // Only custom types currently support LifecycleListeners (ie: AttachesToParent)
Expand Down
39 changes: 28 additions & 11 deletions src/CustomProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,6 @@ export type ShadowGeneratorCustomProps = {
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;
Expand All @@ -72,9 +61,37 @@ export type Control3DCustomProps = {
onControlAdded?: (instance: CreatedInstance<any>) => void
} & CustomProps;

/**
* The below Custom Props are added explicitly and not automatically by inheritance, so do not need union type "& CustomProps"
*/


export type ADTCustomProps = {
/**
* Only applicable for AdvanceDynamicTexture to attach to a mesh. ADT.CreateForMesh(parent, ...)
*/
createForParentMesh?: boolean
};

export type GlowLayerCustomProps = {
/**
* Adds all child nodes to the glow layer.
*/
addIncludeOnlyChildren?: boolean
};

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

/**
* A union of all CustomProps as a convenience typing and easier maintenance in other areas of code (ie: CreatedInstance and HostConfig)
*/
export type AnyCustomProps = CustomProps & (ADTCustomProps & Control3DCustomProps & GlowLayerCustomProps & VirtualKeyboardCustomProps & ShadowGeneratorCustomProps & MaterialCustomProps)
10 changes: 7 additions & 3 deletions src/ReactBabylonJSHostConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as GENERATED from './generatedCode';
import * as CUSTOM_HOSTS from './customHosts';

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

let customProps: CustomProps & (Control3DCustomProps | ADTCustomProps | VirtualKeyboardCustomProps | ShadowGeneratorCustomProps | MaterialCustomProps) = {
// TODO: define this type as an export.
let customProps: AnyCustomProps = {
// Control3D
childrenAsContent: props.childrenAsContent === true, // ie: Button3D.container instead of .addControl()
// AdvancedDynamicTexture
Expand All @@ -347,7 +348,8 @@ const ReactBabylonJSHostConfig: HostConfig<
attachToMeshesByName: props.attachToMeshesByName, // for materials - otherwise will attach to first parent that accepts materials
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
disposeInstanceOnUnmount: props.assignFrom === undefined,
addIncludeOnlyChildren: props.addIncludeOnlyChildren === true,
};

if (customProps.assignFrom !== undefined) {
Expand Down Expand Up @@ -456,6 +458,8 @@ const ReactBabylonJSHostConfig: HostConfig<
metadataLifecycleListenerName = 'GUI2DControl';
} else if (metadata.isCamera === true) {
metadataLifecycleListenerName = 'Camera';
} else if (metadata.isMesh === true) {
metadataLifecycleListenerName = 'AbstractMesh';
} else if (metadata.isNode === true) {
metadataLifecycleListenerName = 'Node';
} else if (metadata.isBehavior === true) {
Expand Down
43 changes: 43 additions & 0 deletions src/customHosts/AbstractMeshLifecycleListener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Mesh } from '@babylonjs/core';
import { GlowLayer } from '@babylonjs/core/Layers/glowLayer';
import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';

import { CreatedInstance } from '../CreatedInstance';
import { FiberAbstractMeshProps } from '../generatedProps';
import BaseLifecycleListener from './BaseLifecycleListener';

export default class AbstractMeshLifecycleListener extends BaseLifecycleListener<AbstractMesh, FiberAbstractMeshProps> {

onMount(instance?: CreatedInstance<AbstractMesh>) {
if (instance === undefined || instance.hostInstance === undefined) {
console.error('Missing instance');
return;
}

let mesh: AbstractMesh = instance.hostInstance;
let tmp: CreatedInstance<any> | null = instance.parent;

while (tmp !== null) {
if (tmp.metadata && tmp.metadata.isGlowLayer === true) {
if (tmp.customProps.addIncludeOnlyChildren === true) {
// TODO: listen for mesh disposal to remove from inclusion list?
(tmp.hostInstance as GlowLayer).addIncludedOnlyMesh(mesh as Mesh);
}
break;
}
tmp = tmp.parent;
}
}

/**
* This was copied from 'NodeLifecycleListener'. TODO: Would be better to have inheritance hierarchy like 'BaseLifecycleListener'.
*/
onParented(parent: CreatedInstance<any>, child: CreatedInstance<any>) {
super.onParented(parent, child);
if (parent.metadata.isNode && child.metadata.isNode) {
// TODO: consider add option for setParent(), which parents and maintains mesh pos/rot in world space
// child.hostInstance.setParent(parent.hostInstance)
child.hostInstance.parent = parent.hostInstance;
}
}
}
1 change: 1 addition & 0 deletions src/customHosts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export { default as BehaviorLifecycleListener } from './BehaviorsLifecycleListen
export { default as FallbackLifecycleListener } from './FallbackLifecycleListener';
export { default as ViewportLifecycleListener } from './ViewportLifecycleListener';
export { default as EngineViewLifecycleListener} from './EngineViewLifecycleListener';
export { default as AbstractMeshLifecycleListener} from './AbstractMeshLifecycleListener';

// These are only used by the reconciler. We export them as strings (as we do for all generated code as well)
// For declaring your own custom components externally you just need the 'string' version from /exportedCustomComponents
Expand Down
3 changes: 3 additions & 0 deletions src/generatedCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9605,6 +9605,7 @@ export class FiberEffectLayer implements HasPropsHandlers<FiberEffectLayerProps>
};
public static readonly Metadata: CreatedInstanceMetadata = {
"isEffectLayer": true,
"isGlowLayer": false,
"className": "FiberEffectLayer"
};
}
Expand Down Expand Up @@ -9673,6 +9674,7 @@ export class FiberGlowLayer implements HasPropsHandlers<FiberEffectLayerProps> {
};
public static readonly Metadata: CreatedInstanceMetadata = {
"isEffectLayer": true,
"isGlowLayer": true,
"className": "FiberGlowLayer"
};
}
Expand Down Expand Up @@ -9745,6 +9747,7 @@ export class FiberHighlightLayer implements HasPropsHandlers<FiberEffectLayerPro
};
public static readonly Metadata: CreatedInstanceMetadata = {
"isEffectLayer": true,
"isGlowLayer": false,
"className": "FiberHighlightLayer"
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/generatedProps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Key, ReactNode, Ref } from "react";
import { ADTCustomProps, Control3DCustomProps, CustomProps, MaterialCustomProps, ShadowGeneratorCustomProps, VirtualKeyboardCustomProps, VRExperienceHelperCustomProps } from "./CustomProps";
import { ADTCustomProps, Control3DCustomProps, CustomProps, GlowLayerCustomProps, MaterialCustomProps, ShadowGeneratorCustomProps, VirtualKeyboardCustomProps, VRExperienceHelperCustomProps } from "./CustomProps";
import { DynamicTerrain as ExtensionsDynamicTerrain } from "./extensions/DynamicTerrain";
import { AbstractScene as BabylonjsCoreAbstractScene } from "@babylonjs/core/abstractScene";
import { Scene as BabylonjsCoreScene } from "@babylonjs/core/scene";
Expand Down Expand Up @@ -395,7 +395,7 @@ declare global {
holographicButton: FiberHolographicButtonProps & FiberHolographicButtonPropsCtor & BabylonNode<BabylonjsGuiHolographicButton>;
meshButton3D: FiberMeshButton3DProps & FiberMeshButton3DPropsCtor & BabylonNode<BabylonjsGuiMeshButton3D>;
effectLayer: FiberEffectLayerProps & FiberEffectLayerPropsCtor & BabylonNode<BabylonjsCoreEffectLayer>;
glowLayer: FiberGlowLayerProps & FiberGlowLayerPropsCtor & BabylonNode<BabylonjsCoreGlowLayer>;
glowLayer: FiberGlowLayerProps & FiberGlowLayerPropsCtor & BabylonNode<BabylonjsCoreGlowLayer> & GlowLayerCustomProps;
highlightLayer: FiberHighlightLayerProps & FiberHighlightLayerPropsCtor & BabylonNode<BabylonjsCoreHighlightLayer>;
thinTexture: FiberThinTextureProps & FiberThinTexturePropsCtor & BabylonNode<BabylonjsCoreThinTexture>;
baseTexture: FiberBaseTextureProps & FiberBaseTexturePropsCtor & BabylonNode<BabylonjsCoreBaseTexture>;
Expand Down
144 changes: 144 additions & 0 deletions stories/babylonjs/SpecialFX/multi-glow-layer.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React, { useRef } from 'react'
import '@babylonjs/inspector'
import { Engine, Scene, useScene } from '../../../dist/react-babylonjs'
import { Color3, Color4, Vector3 } from '@babylonjs/core/Maths/math'
import '../../style.css'
import ScaledModelWithProgress from '../ScaledModelWithProgress'
import { Control } from '@babylonjs/gui/2D/controls/control'

export default { title: 'Special FX' };

/**
* some inspiration derived from here - would be good to extend this sample with more cubes at least.
* https://playground.babylonjs.com/#129LNB#16
*/

const onSceneCreated = (scene) => {
scene.imageProcessingConfiguration.contrast = 1.6;
scene.imageProcessingConfiguration.exposure = 0.6;
scene.imageProcessingConfiguration.toneMappingEnabled = true;
}

const Inspector = () => {
const scene = useScene();
scene.debugLayer.show();
return null;
}

const RADIUS = 10;
const NUMBER_OF_BOXES = 20;

/**
* TODO
* loading so slow
*/
function WithGlowLayer() {
const glow1Ref = useRef(null);
const glow2Ref = useRef(null);

const onCheckbox1Clicked = (value) => {
if (glow1Ref.current) {
glow1Ref.current.isEnabled = value;
}
};

const onCheckbox2Clicked = (value) => {
if (glow2Ref.current) {
glow2Ref.current.isEnabled = value;
}
};

return (
<Engine antialias adaptToDeviceRatio canvasId='babylonJS'>
<Scene clearColor={new Color4(0.02, 0.022, 0.02, 1)} onCreated={onSceneCreated}>
<arcRotateCamera
name='Camera'
alpha={2.5}
beta={0.9}
radius={25}
lowerRadiusLimit={20}
upperRadiusLimit={80}
target={Vector3.Zero()}
useAutoRotationBehavior
/>

<hemisphericLight name='hemi' direction={Vector3.Up()} />
<Inspector />
<glowLayer ref={glow1Ref} name="glow" options={{ mainTextureSamples: 4 }} intensity={1} isEnabled={true} addIncludeOnlyChildren>
{Array.from(new Array(NUMBER_OF_BOXES), (_, index) => index).map(x => (
<box name={'glow-box-1-{number}'} position={new Vector3(Math.cos(2*Math.PI/NUMBER_OF_BOXES*x)*RADIUS,1, Math.sin(2*Math.PI/NUMBER_OF_BOXES*x)*RADIUS)}>
<standardMaterial diffuseColor={Color3.Red()} emissiveColor={Color3.Red()} />
</box>
))}
<ScaledModelWithProgress rootUrl='https://www.babylonjs.com/Assets/NeonPipe/glTF/' sceneFilename='NeonPipe.gltf?v=1'
progressBarColor={Color3.FromInts(255, 165, 0)} center={new Vector3(5, 0, 5)}
/>
</glowLayer>

<glowLayer ref={glow2Ref} name="glow2" options={{ mainTextureSamples: 4 }} intensity={0.4} isEnabled={true} addIncludeOnlyChildren>
{Array.from(new Array(NUMBER_OF_BOXES), (_, index) => index).map(x => (
<box name={'glow-box-1-{number}'} position={new Vector3(Math.cos(2*Math.PI/NUMBER_OF_BOXES*x)*RADIUS,3, Math.sin(2*Math.PI/NUMBER_OF_BOXES*x)*RADIUS)}>
<standardMaterial diffuseColor={Color3.Green()} emissiveColor={Color3.Green()} />
</box>
))}
<ScaledModelWithProgress rootUrl='https://www.babylonjs.com/Assets/NeonPipe/glTF/' sceneFilename='NeonPipe.gltf?v=2'
progressBarColor={Color3.FromInts(255, 165, 0)} center={new Vector3(-5, 0, -5)}
/>
</glowLayer>

<adtFullscreenUi name='ui1'>
<stackPanel
width='200px'
height='200px'
horizontalAlignment={Control.HORIZONTAL_ALIGNMENT_RIGHT}
verticalAlignment={Control.VERTICAL_ALIGNMENT_CENTER}
>
<rectangle height='80px'>
<stackPanel
paddingLeftInPixels={20}
width='200px'
isVertical={false}
horizontalAlignment={Control.HORIZONTAL_ALIGNMENT_RIGHT}
verticalAlignment={Control.VERTICAL_ALIGNMENT_CENTER}
>
<checkbox width='20px' height='20px' isChecked={true} color='green'
onIsCheckedChangedObservable={onCheckbox1Clicked}
/>
<textBlock text='Glow 1 Enabled' width='180px' paddingLeft='5px' color='white'
textHorizontalAlignment={Control.HORIZONTAL_ALIGNMENT_LEFT}
/>
</stackPanel>
</rectangle>
<rectangle height='80px'>
<stackPanel
width='200px'
paddingLeftInPixels={20}
isVertical={false}
horizontalAlignment={Control.HORIZONTAL_ALIGNMENT_RIGHT}
verticalAlignment={Control.VERTICAL_ALIGNMENT_CENTER}
>
<checkbox width='20px' height='20px' isChecked={true} color='green'
onIsCheckedChangedObservable={onCheckbox2Clicked}
/>
<textBlock text='Glow 2 Enabled' width='180px' paddingLeft='5px' color='white'
textHorizontalAlignment={Control.HORIZONTAL_ALIGNMENT_LEFT}
/>
</stackPanel>
</rectangle>
</stackPanel>
</adtFullscreenUi>
<environmentHelper options={{
groundSize: 160,
skyboxSize: 160,
sizeAuto: false
}} setMainColor={[Color3.Gray()]} />
</Scene>
</Engine>
)
}

export const MultiGlowLayer = () => (
<div style={{ flex: 1, display: 'flex' }}>
<WithGlowLayer />
</div>
)
10 changes: 7 additions & 3 deletions tools/generate-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ console.log('ver:', ts.version);

const CONFLICT_INTRINSIC_ELEMENTS = ['Button', 'Ellipse', 'Image', 'Line', 'Polygon'];

const ALL_CUSTOM_PROPS = ['ADTCustomProps', 'Control3DCustomProps', 'CustomProps', 'MaterialCustomProps', 'ShadowGeneratorCustomProps', 'VirtualKeyboardCustomProps', 'VRExperienceHelperCustomProps'];
const ALL_CUSTOM_PROPS = ['ADTCustomProps', 'Control3DCustomProps', 'CustomProps', 'GlowLayerCustomProps', 'MaterialCustomProps', 'ShadowGeneratorCustomProps', 'VirtualKeyboardCustomProps', 'VRExperienceHelperCustomProps'];

// would be good to check JSX.IntrinsicElements with 'keyof', but it's erased at runtime (doesn't work on dynamic strings)
// fixes TS warning: Property 'polygon' must be of type SVGProps<SVGPolygonElement>, but here has type..., so we are skipping to generate polygon for now.
Expand Down Expand Up @@ -143,6 +143,7 @@ const PROPS_EXPORTS: string[] = []; // used to put all props in single import.
*/
const additionalCustomProps: Record<string, string> = {
[`${CLASS_NAME_PREFIX}AdvancedDynamicTexture`]: 'ADTCustomProps',
[`${CLASS_NAME_PREFIX}GlowLayer`]: 'GlowLayerCustomProps',
[`${CLASS_NAME_PREFIX}VirtualKeyboard`]: 'VirtualKeyboardCustomProps',
};

Expand Down Expand Up @@ -1619,12 +1620,15 @@ const generateCode = async () => {
}

if (classesOfInterest.get("EffectLayer")) {
createClassesInheritedFrom(generatedCodeSourceFile, generatedPropsSourceFile, classesOfInterest.get("EffectLayer")!, () => ({ isEffectLayer: true }));
const metadataFromClassName = (className: string) => ({
isEffectLayer: true,
isGlowLayer: className === `${CLASS_NAME_PREFIX}GlowLayer`
});
createClassesInheritedFrom(generatedCodeSourceFile, generatedPropsSourceFile, classesOfInterest.get("EffectLayer")!, metadataFromClassName);
}

if (classesOfInterest.get("ThinTexture")) {
const ADT_CLASSNAME = `${CLASS_NAME_PREFIX}AdvancedDynamicTexture`;
additionalCustomProps[ADT_CLASSNAME] = 'ADTCustomProps';

const fromClassName = (className: string): InstanceMetadataParameter => {
if (className === ADT_CLASSNAME) {
Expand Down

0 comments on commit f5c461b

Please sign in to comment.