From 243eae70e8ac5c25d660701b1c51cf4a600f6a33 Mon Sep 17 00:00:00 2001 From: Brian Zinn Date: Thu, 10 Sep 2020 23:25:18 -0700 Subject: [PATCH] add 'fromInstance' support. #81 --- src/ReactBabylonJSHostConfig.ts | 113 ++++++++++-------- src/generatedCode.ts | 14 +++ .../babylonjs/Basic/fromInstance.stories.js | 58 +++++++++ tools/generate-code.ts | 6 +- 4 files changed, 138 insertions(+), 53 deletions(-) create mode 100644 stories/babylonjs/Basic/fromInstance.stories.js diff --git a/src/ReactBabylonJSHostConfig.ts b/src/ReactBabylonJSHostConfig.ts index 6d582a15..c111efd2 100644 --- a/src/ReactBabylonJSHostConfig.ts +++ b/src/ReactBabylonJSHostConfig.ts @@ -299,62 +299,75 @@ const ReactBabylonJSHostConfig: HostConfig< // console.log(`creating: ${createInfoArgs.namespace}.${type}`) let generatedParameters: GeneratedParameter[] = createInfoArgs.parameters + let babylonObject: any | undefined = undefined - // console.log("generated params:", generatedParameters) - - let args = generatedParameters.map(generatedParameter => { - if (Array.isArray(generatedParameter.type)) { - // TODO: if all props are missing, warn if main prop (ie: options) is required. - let newParameter = {} as any - generatedParameter.type.forEach(subParameter => { - let subPropValue = props[subParameter.name] - if (subPropValue === undefined && subParameter.optional === false && generatedParameter.optional === false) { - console.warn("Missing a required secondary property:", subParameter.name) - } else { - newParameter[subParameter.name] = subPropValue - } - }) - return newParameter - } else { - let value = props[generatedParameter.name] - if (value === undefined) { - // NOTE: we removed the hosted Scene component, which needs (generatedParameter.type == "BabylonjsCoreEngine") - // SceneOrEngine type is Scene - if (generatedParameter.type.includes("BabylonjsCoreScene") || (generatedParameter.type === "any" && generatedParameter.name === "scene")) { - // MeshBuild.createSphere(name: string, options: {...}, scene: any) - // console.log('Assigning scene to:', type, generatedParameter) - value = scene - } else if (generatedParameter.optional === false) { - console.warn(`required parameter for ${type} unassigned -> ${generatedParameter.name}:${generatedParameter.type}`); - } - } - - if (value === undefined && generatedParameter.optional === false) { - console.warn(`On ${type} you are missing a non-optional parameter '${generatedParameter.name}' of type '${generatedParameter.type}'`) + if (props.fromInstance !== undefined) { + if(createInfoArgs.namespace.startsWith('@babylonjs/')) { + const clazz: any = GENERATED.babylonClassFactory(type); + // instanceof will check prototype and derived classes (ie: can assign Mesh instance to a Node) + if (props.fromInstance instanceof clazz) { + babylonObject = props.fromInstance; + } else { + // prevent assigning incorrect type. + console.error('fromInstance wrong type.', props.fromInstance, clazz); } - - return value + } else { + console.error('cannot generate non babylonjs using fromInstance'); } - }) - - let babylonObject: any | undefined = undefined - - if (createInfoArgs.creationType === CreationType.FactoryMethod) { - // console.warn(`creating from Factory: ${createInfoArgs.libraryLocation}.${createInfoArgs.factoryMethod}(...args). args:`, args) - babylonObject = GENERATED.babylonClassFactory(createInfoArgs.libraryLocation)[createInfoArgs.factoryMethod!](...args) } else { - if (metadata.delayCreation !== true) { - if(createInfoArgs.namespace.startsWith('@babylonjs/')) { - const clazz: any = GENERATED.babylonClassFactory(type); - if (clazz === undefined) { - throw new Error(`Cannot generate '${type}' (react-babylonjs):`); + // console.log("generated params:", generatedParameters) + let args = generatedParameters.map(generatedParameter => { + if (Array.isArray(generatedParameter.type)) { + // TODO: if all props are missing, warn if main prop (ie: options) is required. + let newParameter = {} as any + generatedParameter.type.forEach(subParameter => { + let subPropValue = props[subParameter.name] + if (subPropValue === undefined && subParameter.optional === false && generatedParameter.optional === false) { + console.warn("Missing a required secondary property:", subParameter.name) + } else { + newParameter[subParameter.name] = subPropValue } - babylonObject = new clazz(...args) - } else if (createInfoArgs.namespace.startsWith('./extensions/')) { - const extClassName = (GENERATED.intrinsicClassMap as any)[type] || type; - babylonObject = new (BABYLONEXT as any)[extClassName](...args) + }) + return newParameter } else { - console.error("metadata defines (or does not) a namespace that is known", createInfoArgs.namespace) + let value = props[generatedParameter.name] + if (value === undefined) { + // NOTE: we removed the hosted Scene component, which needs (generatedParameter.type == "BabylonjsCoreEngine") + // SceneOrEngine type is Scene + if (generatedParameter.type.includes("BabylonjsCoreScene") || (generatedParameter.type === "any" && generatedParameter.name === "scene")) { + // MeshBuild.createSphere(name: string, options: {...}, scene: any) + // console.log('Assigning scene to:', type, generatedParameter) + value = scene + } else if (generatedParameter.optional === false) { + console.warn(`required parameter for ${type} unassigned -> ${generatedParameter.name}:${generatedParameter.type}`); + } + } + + if (value === undefined && generatedParameter.optional === false) { + console.warn(`On ${type} you are missing a non-optional parameter '${generatedParameter.name}' of type '${generatedParameter.type}'`) + } + + return value + } + }) + + if (createInfoArgs.creationType === CreationType.FactoryMethod) { + // console.warn(`creating from Factory: ${createInfoArgs.libraryLocation}.${createInfoArgs.factoryMethod}(...args). args:`, args) + babylonObject = GENERATED.babylonClassFactory(createInfoArgs.libraryLocation)[createInfoArgs.factoryMethod!](...args) + } else { + if (metadata.delayCreation !== true) { + if(createInfoArgs.namespace.startsWith('@babylonjs/')) { + const clazz: any = GENERATED.babylonClassFactory(type); + if (clazz === undefined) { + throw new Error(`Cannot generate '${type}' (react-babylonjs):`); + } + babylonObject = new clazz(...args) + } else if (createInfoArgs.namespace.startsWith('./extensions/')) { + const extClassName = (GENERATED.intrinsicClassMap as any)[type] || type; + babylonObject = new (BABYLONEXT as any)[extClassName](...args) + } else { + console.error("metadata defines (or does not) a namespace that is known", createInfoArgs.namespace) + } } } } diff --git a/src/generatedCode.ts b/src/generatedCode.ts index 118dbff7..7b1ef96f 100644 --- a/src/generatedCode.ts +++ b/src/generatedCode.ts @@ -482,6 +482,8 @@ export class FiberAbstractMesh implements HasPropsHandlers { }; public static readonly Metadata: CreatedInstanceMetadata = { "isNode": true, + "acceptsMaterials": true, + "isMesh": true, "className": "FiberMesh" }; } @@ -776,6 +784,8 @@ export class FiberLinesMesh implements HasPropsHandlers }; public static readonly Metadata: CreatedInstanceMetadata = { "isNode": true, + "acceptsMaterials": true, + "isMesh": true, "className": "FiberLinesMesh" }; } @@ -833,6 +843,8 @@ export class FiberGroundMesh implements HasPropsHandlers }; public static readonly Metadata: CreatedInstanceMetadata = { "isNode": true, + "acceptsMaterials": true, + "isMesh": true, "className": "FiberGroundMesh" }; } @@ -908,6 +920,8 @@ export class FiberTrailMesh implements HasPropsHandlers }; public static readonly Metadata: CreatedInstanceMetadata = { "isNode": true, + "acceptsMaterials": true, + "isMesh": true, "className": "FiberTrailMesh" }; } diff --git a/stories/babylonjs/Basic/fromInstance.stories.js b/stories/babylonjs/Basic/fromInstance.stories.js new file mode 100644 index 00000000..71de44c8 --- /dev/null +++ b/stories/babylonjs/Basic/fromInstance.stories.js @@ -0,0 +1,58 @@ +import React, { useState, useMemo } from 'react' +import { Engine, Scene, useBabylonScene } from '../../../dist/react-babylonjs' +import { Vector3, Color3, Color4, MeshBuilder } from '@babylonjs/core' +import '../../style.css' + +export default { title: 'Babylon Basic' }; + +const MyMesh = (props) => { + const [mesh, setMesh] = useState(null); + const scene = useBabylonScene(); + useMemo(() => { + console.log('creating a box with scene', scene); + setMesh(MeshBuilder.CreateBox('test', { size: 1}, scene)); + }, []); + + return ( + <> + {mesh && + + + + } + + ) +} + +export const FromInstance = () => { + const [rotation, setRotation] = useState(new Vector3(0, 0, 0)); + const addRotation = () => { + setRotation((state) => new Vector3(0, rotation.y + Math.PI / 4, 0)); + }; + + + + return ( +
+ + + + + + + + +
+ ); + } diff --git a/tools/generate-code.ts b/tools/generate-code.ts index ee9e9e22..1d5885ef 100644 --- a/tools/generate-code.ts +++ b/tools/generate-code.ts @@ -1493,9 +1493,9 @@ const generateCode = async () => { name: "IntrinsicElements", }) - // This includes Node, which is base class for ie: Camera, Mesh, etc. - createClassesDerivedFrom(generatedCodeSourceFile, generatedPropsSourceFile, classesOfInterest.get("TransformNode")!, {}, undefined); - createClassesInheritedFrom(generatedCodeSourceFile, generatedPropsSourceFile, classesOfInterest.get("AbstractMesh")!, () => ({isNode: true})); + // This includes Node, which is base class for ie: Camera, Mesh, etc. (inheriting from Node would add new things like TextureDome) + createClassesDerivedFrom(generatedCodeSourceFile, generatedPropsSourceFile, classesOfInterest.get("TransformNode")!, { isNode: true }, undefined); + createClassesInheritedFrom(generatedCodeSourceFile, generatedPropsSourceFile, classesOfInterest.get("AbstractMesh")!, () => ({isNode: true, acceptsMaterials: true, isMesh: true})); const extra = (newClassDeclaration: ClassDeclaration, originalClassDeclaration: ClassDeclaration) => { // consider having targetable as metadata.