theatre-js / theatre

Motion design editor for the web
https://www.theatrejs.com
Apache License 2.0
10.78k stars 334 forks source link

Add Editable Materials types #486

Open jo-chemla opened 2 weeks ago

jo-chemla commented 2 weeks ago

Add an editable component for meshBasicMaterial, standard, physical and other material flavors to defaultEditableFactoryConfig.

Threejs materials Generic

Standard

Other

Could also extend to r3f/drei materials

jo-chemla commented 2 weeks ago

Current workaround, adapted from api/r3f:

// Component tree wrapped in SheetProvider
<SheetProvider sheet={demoSheet}>
  <e.mesh theatreKey="Cube">
    <boxGeometry args={[1, 1, 1]} />
      {/* <meshStandardMaterial color="orange" theatreKey="Mat" /> */}
      <MyMaterial />
  </e.mesh>
</SheetProvider>

// Custom Material or any threejs not currently supported by theatre, use an existing editable thing like group, and connect one-or-many additionalProps to the threejs type attributes
const MyMaterial = () => {
  // A reference to the THREE.js object
  const threeRef = useRef(new THREE.MeshBasicMaterial({color: 'red'}))

  const [
    // The Theatre.js object that represents our THREE.js object. It'll be initially `null`.
    theatreObject,
    setTheatreObject,
  ] =
    // Let's use `useState()` so our `useEffect()` will re-run when `theatreObject` changes
    useState(null)

  // This `useEffect()` will run when `theatreObject` changes
  useEffect(
    () => {
      // if `theatreObject` is `null`, we don't need to do anything
      if (!theatreObject) return

      const unsubscribe = theatreObject.onValuesChange((newValues) => {
        // Apply the props to our THREE.js object
        threeRef.current.color = newValues.color;
      })
      // unsubscribe from the listener when the component unmounts
      return unsubscribe
    },
    // We only want to run this `useEffect()` when `theatreObject` changes
    [theatreObject],
  )

  return (
    <>
      <e.group
        theatreKey="Material"
        // We're defining one additional property, `offset`, which is not part of THREE.js
        additionalProps={{
          color: types.rgba({ r: 255, g: 0, b: 0, a: 1 }),
        }}
        // a reference to the THREE.js object
        // ref={threeRef}
        // a reference to the Theatre.js object
        objRef={setTheatreObject}
      />
      <meshBasicMaterial 
        ref={threeRef} 
        // color={threeRef.current.offset}
      />
    </>
  )
}

Note: hurdles of this method: