Skip to content

Commit

Permalink
add useLoader hook (contribution from Hookex react-babylonjs fork)
Browse files Browse the repository at this point in the history
move customComponents to hostComponents (except for Skybox).
Add that texture can be assigned to a model (will need to do so for materials as well).
  • Loading branch information
brianzinn committed Jun 3, 2020
1 parent 7877c80 commit 1d89ed5
Show file tree
Hide file tree
Showing 29 changed files with 304 additions and 153 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-babylonjs",
"version": "2.2.1-beta.1",
"version": "2.2.1-beta.2",
"description": "React for Babylon.js",
"homepage": "https://brianzinn.github.io/react-babylonjs/",
"keywords": [
Expand Down
23 changes: 11 additions & 12 deletions src/ReactBabylonJSHostConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {Scene, Engine, Nullable, Node} from '@babylonjs/core'
import * as BABYLONEXT from "./extensions"
import * as GENERATED from './generatedCode'
import * as CUSTOM_HOSTS from "./customHosts"
import * as CUSTOM_COMPONENTS from "./customComponents"

import { FiberModel, LoadedModel } from "./model"
import { CreatedInstance, CreatedInstanceMetadata, CustomProps } from "./CreatedInstance"
Expand Down Expand Up @@ -278,7 +277,7 @@ const ReactBabylonJSHostConfig: HostConfig<
parent: null,
children: [],
propsHandlers: new FiberModel() as any,
lifecycleListener: new CUSTOM_COMPONENTS.ModelLifecycleListener(scene! /* should always be available */, props),
lifecycleListener: new CUSTOM_HOSTS.ModelLifecycleListener(scene! /* should always be available */, props),
customProps: {}
}

Expand Down Expand Up @@ -385,25 +384,25 @@ const ReactBabylonJSHostConfig: HostConfig<

// Consider these being dynamically attached to a list, much like PropsHandlers<T>
if (metadata.isMaterial === true) {
lifecycleListener = new CUSTOM_COMPONENTS.MaterialsLifecycleListener()
lifecycleListener = new CUSTOM_HOSTS.MaterialsLifecycleListener()
} else if (metadata.isGUI3DControl === true) {
lifecycleListener = new CUSTOM_COMPONENTS.GUI3DControlLifecycleListener(scene)
lifecycleListener = new CUSTOM_HOSTS.GUI3DControlLifecycleListener(scene)
} else if (metadata.isGUI2DControl === true) {
lifecycleListener = new CUSTOM_COMPONENTS.GUI2DControlLifecycleListener()
lifecycleListener = new CUSTOM_HOSTS.GUI2DControlLifecycleListener()
} else if (metadata.isTexture === true) {
lifecycleListener = new CUSTOM_COMPONENTS.TexturesLifecycleListener()
lifecycleListener = new CUSTOM_HOSTS.TexturesLifecycleListener()
} else if (metadata.isCamera === true) {
lifecycleListener = new CUSTOM_COMPONENTS.CameraLifecycleListener(scene, props, canvas as HTMLCanvasElement)
lifecycleListener = new CUSTOM_HOSTS.CameraLifecycleListener(scene, props, canvas as HTMLCanvasElement)
} else if (metadata.isNode) {
lifecycleListener = new CUSTOM_COMPONENTS.NodeLifecycleListener();
lifecycleListener = new CUSTOM_HOSTS.NodeLifecycleListener();
} else if (metadata.isBehavior) {
lifecycleListener = new CUSTOM_COMPONENTS.BehaviorLifecycleListener();
lifecycleListener = new CUSTOM_HOSTS.BehaviorLifecycleListener();
}

// here we dynamically assign listeners for specific types.
// TODO: need to double-check because we are using 'camelCase'
if ((CUSTOM_COMPONENTS as any)[underlyingClassName + "LifecycleListener"] !== undefined) {
lifecycleListener = new (CUSTOM_COMPONENTS as any)[underlyingClassName + "LifecycleListener"](scene, props)
if ((CUSTOM_HOSTS as any)[underlyingClassName + "LifecycleListener"] !== undefined) {
lifecycleListener = new (CUSTOM_HOSTS as any)[underlyingClassName + "LifecycleListener"](scene, props)
}

let createdReference = createCreatedInstance(underlyingClassName, babylonObject, fiberObject, metadata, customProps, lifecycleListener)
Expand All @@ -414,7 +413,7 @@ const ReactBabylonJSHostConfig: HostConfig<

// Here we dynamically attach known props handlers. Will be adding more in code generation for GUI - also for lifecycle mgmt.
if (createdReference.metadata && createdReference.metadata.isTargetable === true) {
fiberObject.addPropsHandler(new CUSTOM_COMPONENTS.TargetPropsHandler(scene!))
fiberObject.addPropsHandler(new CUSTOM_HOSTS.TargetPropsHandler(scene!))
}

if (metadata.delayCreation !== true) {
Expand Down
19 changes: 2 additions & 17 deletions src/customComponents/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
export {
default as AdvancedDynamicTextureLifecycleListener, ADTFullscreenUILifecycleListener
} from "./AdvancedDynamicTextureLifecycleListener"
export {default as CameraLifecycleListener} from "./CameraLifecycleListener"
export {default as EnvironmentHelperLifecycleListener} from "./EnvironmentHelperLifecycleListener"
export {default as GUI2DControlLifecycleListener} from "./GUI2DControlLifecycleListener"
export {default as GUI3DControlLifecycleListener} from "./GUI3DControlLifecycleListener"
export {default as GUI3DManagerLifecycleListener} from "./GUI3DManagerLifecycleListener"
export {default as MaterialsLifecycleListener} from "./MaterialsLifecycleListener"
export {default as ModelLifecycleListener} from "./ModelLifecycleListener"
export {default as PhysicsImpostorLifecycleListener} from "./PhysicsImpostorLifecycleListener"
export {default as ShadowGeneratorLifecycleListener} from "./ShadowGeneratorLifecycleListener"
// These are class and functional React components (not "host components" like <standardMaterial> or <box>)
export {default as ModelLifecycleListener} from "../customHosts/ModelLifecycleListener"
export {default as Skybox} from "./Skybox"
export {default as TargetPropsHandler} from "./TargetPropsHandler"
export {default as TexturesLifecycleListener} from "./TexturesLifecycleListener"
export {default as VRExperienceHelperLifecycleListener} from "./VRExperienceHelperLifecycleListener"
export {default as NodeLifecycleListener} from "./NodeLifecycleListener";
export {default as BehaviorLifecycleListener} from "./BehaviorsLifecycleListener";
Original file line number Diff line number Diff line change
@@ -1,108 +1,108 @@
import { CreatedInstance } from "../CreatedInstance"
import { LifecycleListener } from "../LifecycleListener"
import { Color3, Scene, StandardMaterial, Mesh } from "@babylonjs/core"
import { FiberAdvancedDynamicTextureProps } from "../generatedProps"
import { AdvancedDynamicTexture } from "@babylonjs/gui/2D/advancedDynamicTexture";



export default class AdvancedDynamicTextureLifecycleListener implements LifecycleListener<AdvancedDynamicTexture> {
protected props: FiberAdvancedDynamicTextureProps;
protected scene: Scene

constructor(scene: Scene, props: any) {
this.scene = scene
this.props = props
}

onParented(parent: CreatedInstance<any>, child: CreatedInstance<any>): any { /* empty */}

onChildAdded(child: CreatedInstance<any>, parent: CreatedInstance<any>): any { /* empty */}

onMount(instance: CreatedInstance<AdvancedDynamicTexture>): void {
this.addControls(instance)

if (instance.customProps.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
// console.error('we will be attaching the mesh:', mesh.name, mesh);

const material = new StandardMaterial("AdvancedDynamicTextureMaterial", mesh.getScene())
material.backFaceCulling = false
material.diffuseColor = Color3.Black()
material.specularColor = Color3.Black()

if(instance.hostInstance === undefined) {
console.error('missing instance')
} else {
if (this.props.hasAlpha) {
material.diffuseTexture = instance.hostInstance
material.emissiveTexture = instance.hostInstance
instance.hostInstance.hasAlpha = true
} else {
material.emissiveTexture = instance.hostInstance
material.opacityTexture = instance.hostInstance
}
}

mesh.material = material

// 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

instance.hostInstance!.attachToMesh(mesh, supportPointerMove)
}
}

addControls(instance: CreatedInstance<AdvancedDynamicTexture>) {
// When there is a panel, it must be added before the children. Otherwise there is no UtilityLayer to attach to.
// This project before 'react-reconciler' was added from parent up the tree. 'react-reconciler' wants to do the opposite.
instance.children.forEach(child => {
if (child.metadata.isGUI2DControl === true) {
instance.hostInstance!.addControl(child.hostInstance)
child.state = { added: true }
}
})

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
}
this.connect(
instance,
root,
controlNames
)
}

instance.children.forEach(child => {
this.addControls(child)
})
}

connect(keyboard: CreatedInstance<any>, searchInstance: CreatedInstance<any>, controlNames: string[]) {
if (searchInstance.metadata.isGUI2DControl && searchInstance.hostInstance && controlNames.indexOf(searchInstance.hostInstance.name) !== -1) {
// console.log(keyboard.hostInstance, '.connect(->', searchInstance.hostInstance)
keyboard.hostInstance.connect(searchInstance.hostInstance)
}

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

onUnmount(): void {/* empty */}
}

/**
* This is attached by convention in react-reconciler HostConfig.
*/
export class ADTFullscreenUILifecycleListener extends AdvancedDynamicTextureLifecycleListener {/* empty */}
import { CreatedInstance } from "../CreatedInstance"
import { LifecycleListener } from "../LifecycleListener"
import { Color3, Scene, StandardMaterial, Mesh } from "@babylonjs/core"
import { FiberAdvancedDynamicTextureProps } from "../generatedProps"
import { AdvancedDynamicTexture } from "@babylonjs/gui/2D/advancedDynamicTexture";



export default class AdvancedDynamicTextureLifecycleListener implements LifecycleListener<AdvancedDynamicTexture> {
protected props: FiberAdvancedDynamicTextureProps;
protected scene: Scene

constructor(scene: Scene, props: any) {
this.scene = scene
this.props = props
}

onParented(parent: CreatedInstance<any>, child: CreatedInstance<any>): any { /* empty */}

onChildAdded(child: CreatedInstance<any>, parent: CreatedInstance<any>): any { /* empty */}

onMount(instance: CreatedInstance<AdvancedDynamicTexture>): void {
this.addControls(instance)

if (instance.customProps.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
// console.error('we will be attaching the mesh:', mesh.name, mesh);

const material = new StandardMaterial("AdvancedDynamicTextureMaterial", mesh.getScene())
material.backFaceCulling = false
material.diffuseColor = Color3.Black()
material.specularColor = Color3.Black()

if(instance.hostInstance === undefined) {
console.error('missing instance')
} else {
if (this.props.hasAlpha) {
material.diffuseTexture = instance.hostInstance
material.emissiveTexture = instance.hostInstance
instance.hostInstance.hasAlpha = true
} else {
material.emissiveTexture = instance.hostInstance
material.opacityTexture = instance.hostInstance
}
}

mesh.material = material

// 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

instance.hostInstance!.attachToMesh(mesh, supportPointerMove)
}
}

addControls(instance: CreatedInstance<AdvancedDynamicTexture>) {
// When there is a panel, it must be added before the children. Otherwise there is no UtilityLayer to attach to.
// This project before 'react-reconciler' was added from parent up the tree. 'react-reconciler' wants to do the opposite.
instance.children.forEach(child => {
if (child.metadata.isGUI2DControl === true) {
instance.hostInstance!.addControl(child.hostInstance)
child.state = { added: true }
}
})

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
}
this.connect(
instance,
root,
controlNames
)
}

instance.children.forEach(child => {
this.addControls(child)
})
}

connect(keyboard: CreatedInstance<any>, searchInstance: CreatedInstance<any>, controlNames: string[]) {
if (searchInstance.metadata.isGUI2DControl && searchInstance.hostInstance && controlNames.indexOf(searchInstance.hostInstance.name) !== -1) {
// console.log(keyboard.hostInstance, '.connect(->', searchInstance.hostInstance)
keyboard.hostInstance.connect(searchInstance.hostInstance)
}

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

onUnmount(): void {/* empty */}
}

/**
* This is attached by convention in react-reconciler HostConfig.
*/
export class ADTFullscreenUILifecycleListener extends AdvancedDynamicTextureLifecycleListener {/* empty */}
File renamed without changes.
2 changes: 1 addition & 1 deletion src/model/Model.ts → src/customHosts/LoadedModel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IParticleSystem, Skeleton, AnimationGroup, AbstractMesh, Nullable, Vector3, BoundingInfo } from "@babylonjs/core"
import { IParticleSystem, Skeleton, AnimationGroup, AbstractMesh, Nullable, Vector3, BoundingInfo, SceneLoaderProgressEvent } from "@babylonjs/core"
import "@babylonjs/loaders"

export enum LoaderStatus {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class TexturesLifecycleListener implements LifecycleListener<Text
let tmp: CreatedInstance<any> | null = instance.parent

while (tmp !== null) {
if (tmp.metadata && tmp.metadata.isMaterial === true) {
if (tmp.metadata && (tmp.metadata.isMaterial === true || tmp.metadata.className === 'Model')) {
if (assignTo) {
assignProperty(texture, tmp.hostInstance, assignTo);
} else {
Expand Down
22 changes: 21 additions & 1 deletion src/customHosts/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
// With respect to renderers there are two types of react components:
// Composite components ie: <MyComponent> Regular react components that use host components.
// Host Components: Platform-specific components used in lower-case (ie: <standardMaterial> or <box>)
export {
default as AdvancedDynamicTextureLifecycleListener, ADTFullscreenUILifecycleListener
} from "./AdvancedDynamicTextureLifecycleListener"
export { default as CameraLifecycleListener } from "./CameraLifecycleListener"
export { default as EnvironmentHelperLifecycleListener } from "./EnvironmentHelperLifecycleListener"
export { default as GUI2DControlLifecycleListener } from "./GUI2DControlLifecycleListener"
export { default as GUI3DControlLifecycleListener } from "./GUI3DControlLifecycleListener"
export { default as GUI3DManagerLifecycleListener } from "./GUI3DManagerLifecycleListener"
export { default as MaterialsLifecycleListener } from "./MaterialsLifecycleListener"
export { default as ModelLifecycleListener } from "./ModelLifecycleListener"
export { default as PhysicsImpostorLifecycleListener } from "./PhysicsImpostorLifecycleListener"
export { default as ShadowGeneratorLifecycleListener } from "./ShadowGeneratorLifecycleListener"
export { default as TargetPropsHandler } from "./TargetPropsHandler"
export { default as TexturesLifecycleListener } from "./TexturesLifecycleListener"
export { default as VRExperienceHelperLifecycleListener } from "./VRExperienceHelperLifecycleListener"
export { default as NodeLifecycleListener } from "./NodeLifecycleListener";
export { default as BehaviorLifecycleListener } from "./BehaviorsLifecycleListener";

// 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
export { default as HostWithEventsFiber } from "./hostWithEventsFiber"

export const HostWithEvents: string = "HostWithEvents"
2 changes: 1 addition & 1 deletion src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export const useHover = (over?: HoverType, out?: HoverType): [MutableRefObject<C
if (observer2dGuiEnter !== null) {
const control = ref.current.hostInstance as Control;
control.onPointerEnterObservable.remove(observer2dGuiEnter);
control.onPointerOutObservable.remove(observer2dGuiEnter);
control.onPointerOutObservable.remove(observer2dGuiOut);
observer2dGuiEnter = null;
observer2dGuiOut = null;
}
Expand Down
Loading

0 comments on commit 1d89ed5

Please sign in to comment.