Giter Site home page Giter Site logo

pmndrs / react-three-fiber Goto Github PK

View Code? Open in Web Editor NEW
26.0K 26.0K 1.5K 23.92 MB

๐Ÿ‡จ๐Ÿ‡ญ A React renderer for Three.js

Home Page: https://docs.pmnd.rs/react-three-fiber

License: MIT License

JavaScript 0.36% TypeScript 99.52% Shell 0.02% HTML 0.11%
3d animation fiber react renderer threejs

react-three-fiber's Introduction

@react-three/fiber

Version Downloads Twitter Discord Open Collective ETH BTC

react-three-fiber is a React renderer for threejs.

Build your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can participate in React's ecosystem.

npm install three @types/three @react-three/fiber

Does it have limitations?

None. Everything that works in Threejs will work here without exception.

Is it slower than plain Threejs?

No. There is no overhead. Components render outside of React. It outperforms Threejs in scale due to Reacts scheduling abilities.

Can it keep up with frequent feature updates to Threejs?

Yes. It merely expresses Threejs in JSX, <mesh /> dynamically turns into new THREE.Mesh(). If a new Threejs version adds, removes or changes features, it will be available to you instantly without depending on updates to this library.

What does it look like?

Let's make a re-usable component that has its own state, reacts to user-input and participates in the render-loop. (live demo).
import { createRoot } from 'react-dom/client'
import React, { useRef, useState } from 'react'
import { Canvas, useFrame } from '@react-three/fiber'

function Box(props) {
  // This reference gives us direct access to the THREE.Mesh object
  const ref = useRef()
  // Hold state for hovered and clicked events
  const [hovered, hover] = useState(false)
  const [clicked, click] = useState(false)
  // Subscribe this component to the render-loop, rotate the mesh every frame
  useFrame((state, delta) => (ref.current.rotation.x += delta))
  // Return the view, these are regular Threejs elements expressed in JSX
  return (
    <mesh
      {...props}
      ref={ref}
      scale={clicked ? 1.5 : 1}
      onClick={(event) => click(!clicked)}
      onPointerOver={(event) => hover(true)}
      onPointerOut={(event) => hover(false)}>
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
    </mesh>
  )
}

createRoot(document.getElementById('root')).render(
  <Canvas>
    <ambientLight intensity={Math.PI / 2} />
    <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />
    <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
    <Box position={[-1.2, 0, 0]} />
    <Box position={[1.2, 0, 0]} />
  </Canvas>,
)
Show TypeScript example
npm install @types/three
import * as THREE from 'three'
import { createRoot } from 'react-dom/client'
import React, { useRef, useState } from 'react'
import { Canvas, useFrame, ThreeElements } from '@react-three/fiber'

function Box(props: ThreeElements['mesh']) {
  const ref = useRef<THREE.Mesh>(null!)
  const [hovered, hover] = useState(false)
  const [clicked, click] = useState(false)
  useFrame((state, delta) => (ref.current.rotation.x += delta))
  return (
    <mesh
      {...props}
      ref={ref}
      scale={clicked ? 1.5 : 1}
      onClick={(event) => click(!clicked)}
      onPointerOver={(event) => hover(true)}
      onPointerOut={(event) => hover(false)}>
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
    </mesh>
  )
}

createRoot(document.getElementById('root') as HTMLElement).render(
  <Canvas>
    <ambientLight intensity={Math.PI / 2} />
    <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />
    <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
    <Box position={[-1.2, 0, 0]} />
    <Box position={[1.2, 0, 0]} />
  </Canvas>,
)

Live demo: https://codesandbox.io/s/icy-tree-brnsm?file=/src/App.tsx

Show React Native example

This example relies on react 18 and uses expo-cli, but you can create a bare project with their template or with the react-native CLI.

# Install expo-cli, this will create our app
npm install expo-cli -g
# Create app and cd into it
expo init my-app
cd my-app
# Install dependencies
npm install three @react-three/fiber@beta react@rc
# Start
expo start

Some configuration may be required to tell the Metro bundler about your assets if you use useLoader or Drei abstractions like useGLTF and useTexture:

// metro.config.js
module.exports = {
  resolver: {
    sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs'],
    assetExts: ['glb', 'png', 'jpg'],
  },
}
import React, { useRef, useState } from 'react'
import { Canvas, useFrame } from '@react-three/fiber/native'
function Box(props) {
  const mesh = useRef(null)
  const [hovered, setHover] = useState(false)
  const [active, setActive] = useState(false)
  useFrame((state, delta) => (mesh.current.rotation.x += delta))
  return (
    <mesh
      {...props}
      ref={mesh}
      scale={active ? 1.5 : 1}
      onClick={(event) => setActive(!active)}
      onPointerOver={(event) => setHover(true)}
      onPointerOut={(event) => setHover(false)}>
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
    </mesh>
  )
}
export default function App() {
  return (
    <Canvas>
      <ambientLight intensity={Math.PI / 2} />
      <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />
      <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
      <Box position={[-1.2, 0, 0]} />
      <Box position={[1.2, 0, 0]} />
    </Canvas>
  )
}

Documentation, tutorials, examples

Visit docs.pmnd.rs

First steps

You need to be versed in both React and Threejs before rushing into this. If you are unsure about React consult the official React docs, especially the section about hooks. As for Threejs, make sure you at least glance over the following links:

  1. Make sure you have a basic grasp of Threejs. Keep that site open.
  2. When you know what a scene is, a camera, mesh, geometry, material, fork the demo above.
  3. Look up the JSX elements that you see (mesh, ambientLight, etc), all threejs exports are native to three-fiber.
  4. Try changing some values, scroll through our API to see what the various settings and hooks do.

Some helpful material:

Ecosystem

There is a vibrant and extensive eco system around three-fiber, full of libraries, helpers and abstractions.

Who is using Three-fiber

A small selection of companies and projects relying on three-fiber.

How to contribute

If you like this project, please consider helping out. All contributions are welcome as well as donations to Opencollective, or in crypto BTC: 36fuguTPxGCNnYZSRdgdh6Ea94brCAjMbH, ETH: 0x6E3f79Ea1d0dcedeb33D3fC6c34d2B1f156F2682.

Backers

Thank you to all our backers! ๐Ÿ™

Contributors

This project exists thanks to all the people who contribute.

react-three-fiber's People

Contributors

alaricbaraou avatar aleclarson avatar andersonleite avatar birkir avatar btpoe avatar codyjasonbennett avatar dangrabcad avatar dbismut avatar dependabot[bot] avatar dm385 avatar drcmda avatar giulioz avatar gsimone avatar hasparus avatar hmans avatar itsdouges avatar joshuaellis avatar kmannislands avatar leops avatar methuselah96 avatar mike-dax avatar partynikko avatar ryanking1809 avatar saitonakamura avatar satelllte avatar setsun avatar stephencorwin avatar stryju avatar susiwen8 avatar tirzono avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-three-fiber's Issues

sharing textures/materials/geometries

Sorry if this is noob question but how do I share textures, materials, and geometry?

In normal three.js I can do this

const texture = loader.load('someurl');
const material = new THREE.MeshBasicMaterial({map: texture});
const geometry = new THREE.BoxBufferGeometry(1, 1, 1);

const m1 = new THREE.Mesh(geometry, material);
const m2 = new THREE.Mesh(geometry, material);
const m3 = new THREE.Mesh(geometry, material);
const m4 = new THREE.Mesh(geometry, material);

m1.position.x = -2;
m2.position.x = -1;
m3.position.x = 1;
m4.position.x = 2;

scene.add(m1);
scene.add(m2);
scene.add(m3);
scene.add(m4);

What's the equivalent here?

I get the

<group>
    <mesh position=...>
    <mesh position=...>
    <mesh position=...>
    <mesh position=...>
</group>

part but I'm not sure I get the rest.

in normal DOM React heavy resources are referred to by URLs and the browser magically manages them so

<div>
  <img url="sameUrl" />
  <img url="sameUrl" />
  <img url="sameUrl" />
  <img url="sameUrl" />
</div>

Only actually loads one image into the browser. The browser also knows when that image is safe to free.

It feels like texture, geometry, and materials are kind of the equivalent in three.js but here they're being treated as DOM elements when in fact they are not part of the hierarchy. Like the actual bits of an image they are outside the hierarchy so it seems confusing to me to make them part of the hierarchy.

Optional(?) feature to let users control their own rendering

I have a requestAnimationFrame higher up in my component tree than the Three.js code, to do things like update overlayed GUI elements using state from the game, and drive the updating of my game state (which is independent of the component tree). It appears this library controls its own requestAnimationFrame handler and the only way to hook into it is inside a <Canvas>. What about a way to let the user opt-in to their own animation, or remove it entirely and always make the user call reqAnimFrame? For example with this setup I don't think an end user could do a render-only-one-frame setup to render a static webgl image?

How to animate vertices?

I'm rather stuck on how to animate vertices (geometry) dynamically. I either get a static result or nothing displays. I'm sure I just don't understand how to properly interpolate with three.js and this lib yet... any insight?

function Stage() {
  const [active, setActive] = useState(false);
  const [hovered, setHover] = useState(false);
  const { color, pos, offset, ...props } = useSpring({
    color: active ? 'hotpink' : 'white',
    scale: hovered ? [1.5, 1.5, 1.5] : [1, 1, 1],
    offset: active ? 1.2 : 1,
    config: { mass: 10, tension: 1000, friction: 300, precision: 0.00001 },
  });
  const vertices = [[-1, 0, 0], [0, 1, 0], [1, 0, 0], [0, -1, 0], [-1, 0, 0]];
  return (
    <animated.line
      onClick={e => setActive(!active)}
      onHover={e => setHover(true)}
      onUnhover={e => setHover(false)}
      {...props}>
      <animated.geometry
        name="geometry"
        // verticesNeedUpdate={true}
        vertices={vertices.map(v =>
          offset.interpolate(o => new THREE.Vector3(...v.map(v2 => v2 * o)))
        )}
        // vertices={vertices.map(v => new THREE.Vector3(...v))}
      />
      <animated.lineBasicMaterial name="material" color={color} />
    </animated.line>
  );
}

const Vis = React.memo(() => {
  if (!webgl.isWebGLAvailable()) {
    var WebGLError = webgl.getWebGLErrorMessage();
    return WebGLError;
  }
  console.log('RENDER');
  return (
    <Container>
      <Canvas>
        <Stage />
      </Canvas>
    </Container>
  );
});

<bufferAttributes> example not working?

Hello!

Iโ€™m trying to use <bufferGeometry> with <bufferAttributes> and am failing, nothing renders. I constructed the bufferGeometry based on the attachObject example in Readme.md.

Hereโ€™s a sandbox: https://codesandbox.io/s/2vpv8y2qj0

This works:

  let geometry = new BufferGeometry();
  geometry.addAttribute("position", new BufferAttribute(positions, 3));

  return (
    <points>
      <primitive attach="geometry" object={geometry} />
      <pointsMaterial attach="material" color="blue" size={0.1} />
    </points>
  );

While this doesnโ€™t:

  return (
    <points>
      <bufferGeometry attach="geometry">
        <bufferAttribute
          attachObject={["attributes", "position"]}
          array={positions}
          itemSize={3}
        />
      </bufferGeometry>
      <pointsMaterial attach="material" color="red" size={0.1} />
    </points>
  );

As far as I my understanding goes, the second way is just the declarative version of the first. Can you spot what Iโ€™m doing wrong?

Effect post processing

Hi! I've seen the Lorem Ipsum example and how you handle the glitch effect, but I'm not sure how to replicate this using for example this postprocessing lib.

Here is my attempt:

import {
  PixelationEffect,
  EffectComposer,
  EffectPass,
  RenderPass
} from 'postprocessing'

applySpring({ EffectComposer, RenderPass, EffectPass, PixelationEffect })
applyThree({ EffectComposer, RenderPass, EffectPass, PixelationEffect })

function Effects({ factor }) {
  const { gl, scene, camera, size } = useThree()
  const composer = useRef()

  useEffect(
    () => void composer.current.obj.setSize(size.width, size.height),
    [size.width, size.height]
  )
  useRender(() => composer.current.obj.render(), true)

  const effect = useMemo(() => new PixelationEffect(100.0))
  return (
    <effectComposer ref={composer} args={[gl]}>
      <renderPass name="passes" args={[scene, camera]} />
      <anim.effectPass name="passes"  args={[camera, effect]} renderToScreen />
    </effectComposer>
  )
}

But unfortunately nothing appears on screen, and in any case, I'd like ideally to be able to write:

<anim.pixelationPass name="passes" renderToScreen factor={factor} />

Would I have to create a class similarly to what you did with GlitchPass?
Sorry this is a noob question but I'm curious to see how one could potentially reuse existing filters!

Materials: VertexColor does not work

Hi, ๐Ÿ‘‹

I was using three.js with my own reconciler for a React usage, and I was displaying a Mesh associated to a Material with: 'vertexColors: VertexColors'. It worked.

Now I'm using this code:

[...]
let geometry=...
let material = new MeshStandardMaterial({
                        roughness: 1.0,
                        metalness: 0.0,
                        vertexColors: VertexColors,
                    });
[...]
<Canvas>
    <mesh 
        geometry={geometry}
        material={material}
    />
</Canvas>

But it diplays a black mesh :(
Have you an idea?

Thanks!

โœ๏ธ Roadmap for 2.x (suggestions welcome)

how to load entire meshes/groups?

Sorry if i'm being really stupid here!

I'm trying to load an obj file using three/examples/jsm/loaders/OBJLoader, which i've done following the useMemo example in the readme, but I can't for the life of me work out what I do with the loaded data once I have it.

The obj loader is returning me a Three.Group, how do I then use that inside a jsx heirarchy?

If i was using Three directly, I would just do something like parent.add(loadedObj), but how do i do that here? what i basically want it this:

const obj = someLoadedObj
return (
   <Parent>
      <obj>
      <obj>
      <obj>
   </Parent>
)

Panning / Orbit Controls

I'm trying to implement a panning system on scroll events, and I have managed to get something working but it appears I've done it in the slowest way possible.

I've noticed you're using orbitalControls here (https://codesandbox.io/embed/mo0xrqrj79) and I'm trying to jam it into my system with little success. Are you able to explain a little how that all works?

It looks like the orbital controls tell the renderer how to position the camera, and you need to use useRender to tell the objects to rerender, and. Is that correct?

Here's my very slow attempt a panning (sorry, I couldn't get it rendering correctly on CodeSandbox)

import React, { useMemo, useState } from 'react';
import * as THREE from 'three/src/Three'
import { Canvas } from 'react-three-fiber'
import _ from 'lodash'

function Extrusion({ x, y, width, height, radius, ...props }) {
  const roundedRect = useMemo(() => {
    const roundedRect = new THREE.Shape()
    roundedRect.moveTo(x, y + radius);
    roundedRect.lineTo(x, y + height - radius);
    roundedRect.quadraticCurveTo(x, y + height, x + radius, y + height);
    roundedRect.lineTo(x + width - radius, y + height);
    roundedRect.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
    roundedRect.lineTo(x + width, y + radius);
    roundedRect.quadraticCurveTo(x + width, y, x + width - radius, y);
    roundedRect.lineTo(x + radius, y);
    roundedRect.quadraticCurveTo(x, y, x, y + radius);
    return roundedRect
  }, [x, y, width, height, radius])

  return (
    <mesh>
      <extrudeGeometry name="geometry" args={[roundedRect, props]} />
      <meshNormalMaterial name="material" />
    </mesh>
  )
}

function Bar({ start, end, yIndex }) {
  return <Extrusion
    x={start}
    y={yIndex}
    height={10}
    width={end - start}
    radius={3}
    bevelEnabled={false}
    depth={10}
  />
}

const makeRandomBar = () => {
  const start = _.random(-1000, 1000)
  const length = _.random(50, 500)
  const yIndex = _.random(-1000, 1000)
  return [start, start + length, yIndex]
}

const bars = _.range(0, 250).map(n => makeRandomBar())

function App() {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);
  const scrollFunc = e => {
    setX(x + e.deltaX)
    setY(y + e.deltaY)
  }
  return (
    <div style={{ width: "100%", height: "100%", position: "absolute" }}>
      <Canvas camera={{ position: [0, 0, 300] }} onWheel={scrollFunc}>
        <group position={[x, y, 0]}>
          {bars.map(b => <Bar start={b[0]} end={b[1]} yIndex={b[2]} />)}
        </group>
      </Canvas>
    </div>
  );
}

export default App;

Asking for advice on manage three.js with React and data-driven rendering possibility in react-three-fiber

Hi, thanks for sharing react-three-fiber with developers! I got really a lot thoughts in how to manage three.js application with React from this project.

Currently, I'm going to make a series of data visualization tutorials from scratch, and I also created a repository in GitHub call glmaps. In a word, glmaps is a project full of bunch of earth and map visualization examples based on three.js and deck.gl, as well as some tutorials for visualization beginners, in which I realized a small three.js application manager, too.

The manager I created is quite simple, but can cover the basic needs, such as Curve Animation, I may ask you for advice in better coding, that is how will you organize the codes if you need to realize it.

1. The background and question description

I created a globe with three.js, in which I have a visualization type named "Curve Animation"(Something like arc animation from origin to destination). I handle this requirement with dividing it into two parts currently.

  1. Manage three.js scene with React (with Hooks, Hooks is really good!)
  2. Animate Curve with setRange and force it update

1.1 Manage three.js

My idea is quite simple, I provide a three.js wrapper for react developers (I know it lacks a lot of reactive things):

const ThreeCube = (props) => {
  useEffect(() => {
    if (!data.length) return () => {};

    const [cleanFunc] = threeEntryPoint({
      ...props
    });

    return () => {
      cleanFunc();
    };
  });

  return (
    <canvas 
      id={id}
    />
  );
};

in which developers can handle his three.js application just like write plain JavaScript. In which threeEntryPoint is entry point for SceneManager, and the later is a function which can handle the whole three.js application lifecycle.

const threeEntryPoint = ({id, ...otherProps}) => {
  const canvas = document.getElementById(id);
  const sceneManager = SceneManager.getInstance(canvas, otherProps);
  raf = null;
  
  function bindEventListeners() {
    window.addEventListener ("resize", resizeCanvas);

    resizeCanvas();
  }
  resizeCanvas = function() {
    // ...
  }
  const render = () => {
    // ...
  }

  bindEventListeners();
  render();

  const cleanFunc = () => {
    const gl = canvas.getContext('webgl');
    gl.clearColor(1.0, 1.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    window.removeEventListener("resize", resizeCanvas);
    cancelAnimationFrame(raf);
    SceneManager.destroyInstance();
  }

  return [cleanFunc];
}

1.2 Animate Curve

Definition of Curve (the orange lines in the picture): Curve is actually THREE.Line made with THREE.BufferGeometry (so I can control which part of this line be rendered to the scene, with the help of setRange method)

The structure of my scene is:

  • Put multiple Curve (which is THREE.Line) in THREE.Mesh code
  • Put the THREE.BufferGeometry used in Curve in an array (suppose it's named curveMesh) code
  • Put THREE.Mesh in THREE.Group, and put THREE.Group in THREE.Scene code

and then when I need to update the Curve, I get the array and manipulate it like this:

curveMesh.children.forEach((path) => {
  path.geometry.setDrawRange(index, 20);
  path.geometry.attributes.position.needsUpdate = true;
})

and then back to the title of this issue (sorry for that, but I think only in this way I can express my thoughts completely...).

2. How to make data-driven render possible with react-three-fiber

I wonder how can I do it with react-three-fiber if I want to realize the above animation? According to README, I think I may code it in this way possibly:

import { Canvas } from 'react-three-fiber'

function App(props) {
  const {curvesData = []} = props;
  return (
    <Canvas>
      <group>
        <mesh
          geometry={new THREE.BoxGeometry(1, 1, 1)}
          material={new THREE.MeshBasicMaterial({ color: 0x00ff00, transparent: true })}
        >
			{curvesData.map((curve) => {
				<line
	          	  geometry={new THREE.BufferGeometry()}
	  			  // MY THOUGHTS
				  onUpdate={(instance) => instance.setDrawRange(SOMETHING, SOMETHING, etc)}
		        />
			})}
		</mesh>
      </group>
    </Canvas>
  )
}

but how can I insert setDrawRange method, I'm not sure whether it's right to put it inside a onUpdate callback?

And another question is that what's your consideration in designing the API of react-three-fiber? Since I find you may want developers provide objects like geometry manually, rather than a data-driven API like D3?

The way D3 handle it:

d3.select("body")
  .selectAll("p")
  .data([4, 8, 15, 16, 23, 42])
  .enter().append("p")
    .text(function(d) { return "Iโ€™m number " + d + "!"; });

I supposed react-three-fiber may do it like this (if it's good to do it in this way):

import { Canvas } from 'react-three-fiber'

function App() {
  return (
    <Canvas>
      <group>
        <mesh
          geometry={new THREE.BoxGeometry(1, 1, 1)}
          material={new THREE.MeshBasicMaterial({ color: 0x00ff00, transparent: true })}
          data={data}
		  customRender={(item, index, instance) => {
			// instance: the object itself
			// item: current data item, equals to data[index]
			// index: current data item index
			
			// customized logic here
		  }}
        />
      </group>
    </Canvas>
  )
}

Thanks for your sharing, again! react-three-fiber is really awesome!

MobX Wrapper interfering with events

It looks like I can't get away with using a MobX wrapper with pointer events.
Try clicking on a bar at https://codesandbox.io/s/kz6m4zwxr.
See BarMesh in App.js & BarGeometry.js for the code.

Maybe I can figure out a way of moving Mobx up into the higher levels as a workaround - but perhaps this would affect other use cases as well.

Managing Rendering & State

I'm trying to wrap my head around rendering and state - and it's getting complicated. I'm not sure you have any tips or best practices, but maybe laying out my thought process might be useful for the future development of react-three-fibre - I'm hoping it all makes sense.

I've tried setting up external states for each object using mobx. This is partly to avoid react rendering updates with state change as that is incredibly slow and I want to avoid re-rendering the webGL canvas. Instead of getting react to render I want three to render.

One solution I tried was to update three externally using reactive functions, but unless I modify the object inside the useRender function it is incredibly slow. Maybe this would work better with your planned version 2 updates?

class BarModel {
    @observable start = 0
    @observable end = 0
    @observable yIndex = 0

    constructor({uuid, start, end, yIndex}){
        this.uuid = uuid;
        this.prevX = BarsStore.xUnitWidth
        this.start = start
        this.end = end
        this.yIndex = yIndex
        // update the dome element impirically with autorun events
        this.component = BarMesh({
            uuid: this.uuid,
            x: this.start,
            x2: this.end,
            y: this.yIndex
        })
    }
    updateBarDimensions = autorun(() => {
        const xUnitWidth = BarsStore.xUnitWidth
        if (!this.component.ref.current) return null
        const geometry = this.component.ref.current.geometry
        geometry.attributes.position.array = geometry.attributes.position.array.map((p, i) => {
            // TODO - figure out how to keep radius the same
            if ((i + 3) % 3 === 0) {
                return p * xUnitWidth / this.prevX
            } else {
                return p
            }
        })
        geometry.attributes.position.needsUpdate = true;
        this.prevX = xUnitWidth
    })
}

So what I'm now doing is tracking global state changes, then having the three objects update themselves via the useRender function. I'm starting to see the useRender function getting a little bulky.

export function BarMesh({ uuid, x, x2, y }) {
    const mesh = useRef()
    const { scene } = useThree()
    let prevXWidth = BarsStore.xUnitWidth
    useRender(() => {
        if (BarsStore.xUnitWidth !== prevXWidth) {
            const geometry = mesh.current.geometry
            geometry.attributes.position.array = geometry.attributes.position.array.map((p, i) => {
                // TODO - figure out how to keep radius the same
                if ((i + 3) % 3 === 0) {
                    return p * BarsStore.xUnitWidth / prevXWidth
                } else {
                    return p
                }
            })
            geometry.attributes.position.needsUpdate = true;
            prevXWidth = BarsStore.xUnitWidth
        }
        if (!BarsStore.visibleBars.has(uuid)) {
			// doesn't work but nice try
            scene.remove(mesh.current)
        }
    })
    return (
        <mesh name={uuid} key={uuid} ref={mesh} onClick={() => BarsStore.hideBar(uuid)}>
            <extrudeBufferGeometry 
                name="geometry" 
                args={[
                    getBarShape(x, x2, y), 
                    {depth: 10, bevelEnabled: false}
                ]} />
            <meshNormalMaterial name="material" />
        </mesh>
    )
}

Maybe I could create a list of loop actions that are reactive to state changes to keep things simpler. It might also be useful to access the scene outside of the useThree hook in this scenario.

export function BarMesh({ uuid, x, x2, y }) {
    const mesh = useRef()
    useRender(() => {
        BarsStore.loops[uuid].forEach(fn => fn())
    })
    return (
        <mesh name={uuid} key={uuid} ref={mesh} onClick={() => BarsStore.hideBar(uuid)}>
            <extrudeBufferGeometry 
                name="geometry" 
                args={[
                    getBarShape(x, x2, y), 
                    {depth: 10, bevelEnabled: false}
                ]} />
            <meshNormalMaterial name="material" />
        </mesh>
    )
}

I guess I'm finding react useful for initially laying the objects on the page, and hooking into the three render cycle - but it's hard to avoid imperative code when updating three objects in a performant way, and react rendering gets a bit in the way of the three renderer.

I'm beginning to wonder how different the code would be without react? Is react getting in the way or is it helping? Or maybe I've gone too far down the wrong rabbit hole, and there's a better way to manage state and render updates that's simpler?

Do you have any thoughts on this? (hopefully I'm making sense)

Small Typo in Readme

Hi,

Pretty sure that the occurrences of:

readme.md

userData={ test: "hello" }

Should be this

userData={{ test: "hello" }}

onUpdate api performance

I've been trying to switch to the onUpdate api but it seems to be significantly slower than creating and modifying the three objects and adding them in a primitive. Perhaps I need to provide an onUpdate function to the mesh & material to make it more performant? (Note: I'm using mobX to negate the need for useMemo)

You can see the BarMesh code in BarGeometry.js & App.js here https://codesandbox.io/s/kz6m4zwxr

export const BarMesh = observer(({ bar, ...props }) => {
  const update = self => {
    self.needsUpdate = true;
    self.parent.computeBoundingSphere();
    self.parent.computeBoundingBox();
    self.parent.computeVertexNormals();
  };
  return (
    <mesh {...props}>
      <bufferGeometry attach="geometry">
        <bufferAttribute
          attachObject={["attributes", "position"]}
          array={bar.vertices}
          count={bar.verticesCount}
          itemSize={3}
          onUpdate={update}
        />
      </bufferGeometry>
      <meshNormalMaterial attach="material" />
    </mesh>
  );
});

You can see the same thing using three.js objects in a primitive tag is significantly faster
https://codesandbox.io/s/r4133z2r6o

I haven't able to pinpoint the difference between the two. Interestingly removing the onPointerUp event creates a huge jump in performance - so I wonder if it has anything to do with that (Although the primitive tag option has the same events)

Provide a super-simple "Hello, World!"-like example

First of all, thank you so much for bringing together two awesome libraries -- I have been an adamant user of react and just recently discovered three.js -- mixing the two sounds awesome. I appreciate your work so much!

Something I am missing is a super-simple "Hello, World!"-like example that does not go into the details of the API (like the examples/components files), but only draws a static cube, or something.

The first example in the README could be such a thing, however, I am unsure if those just dummy / pseudo-examples? I tried to copy&paste the first (under "How it looks like"), amending all the missing dependencies, of course, and I got:

screengrab-2019-03-12_10 28 05

... should it look like that? I don't know. Probably not. If you look at the three.js getting started, they build a cube and rotate it. That's it. I fear, what you have as the first example is already too complex. I would love to help by pushing a PR, however I admit, I am still struggling to get a minimum working example running (will probably try to clone the examples/components next).

I believe a dead-simple example like a rotating cube, similar to the three.js getting-started could drive adoption and avoid some frustration.

Best

Timm

Weird canvas dimensions cause blurry render.

You can observe this in the examples, find the canvas DOM node and compare the style dimensions with the canvas height and width attributes. Presumably, the ~2x difference visibly causes a blurry image:

image

Compare this with zooming out by 50%, the render is crisp.

image

I first noticed this in a project of mine that I did in both vanilla three.js and react-three-fiber, both using the exact same geometry, but the latter appearing blurry.

CSS Renderer

Hi, I was wondering if it's possible to combine HTML and THREE elements, maybe using CSS3DRenderer. What may be the best practice?

useThree does not work.

TypeError: Cannot read property 'subscribe' of undefined
useThree
node_modules/react-three-fiber/dist/index.js:577
574 |
575 | function useThree(fn) {
576 | var _useContext2 = useContext(stateContext),

577 | subscribe = _useContext2.subscribe,
578 | props = _objectWithoutPropertiesLoose(_useContext2, ["subscribe"]);
579 |
580 | return props;

e.g. https://codesandbox.io/s/k052wrkp4r

Tests?

do you have in plan to write tests?

Next.js?

I'm trying to use this with next.js. Though I can't because of this error:

import './polyfills.js';
       ^^^^^^^^^^^^^^^^

SyntaxError: Unexpected string

As far as I'm concerned, it's coming from this line: import { Canvas } from 'react-three-fiber';

Anybody else managed to get it working with Next?

directly assigned geometry wrong vertices

I had a group in which one geometry was directly assigned and the other via name.

  <group>
      <anim.line position={pos}>
        <geometry name="geometry" vertices={vertices.map(v => new THREE.Vector3(...v))} />
        <anim.lineBasicMaterial name="material" color={color} />
      </anim.line>}
      <anim.mesh
        {...props}
        geometry={someMemoizedFunctionToCalculateGeometry}
      >
        <meshStandardMaterial name="material" color="grey" transparent />
      </anim.mesh>}
    </group>

This resulted in occasionally having a wrong vertex buffer for the line (rendering an outline of some other geometry) and runaway memory usage (5GB for 10 objects).
Is there anything special to watch out for directly assigning attributes?
I will try to produce a minimal example, but need to extract the rest of our app.

Destroys context

I'm using redux to connect child elements in my scene.

This one took a few hours to track down. I have a simple component connected to redux, like:

@connect(state => ({}))
class MyFriend extends Component {
  render() {
    return null;
  }
}

If I render it outside a <Canvas>, it works fine:

      <div>
        <MyFriend />
        <Canvas />
      </div>

However if I render it inside a Canvas:

      <div>
        <Canvas>
          <MyFriend />
        </Canvas>
      </div>

Then the app crashes:

Uncaught Invariant Violation: Could not find "store" in the context of "Connect(MyFriend)"

I suspect it's related to the custom rendering here, but I'm still digging into it: https://github.com/drcmda/react-three-fiber/blob/master/src/canvas.js#L197-L204

Updating uniforms

Hello!

Iโ€™m trying to update a uniform value used in a fragment shader, but the change doesnโ€™t seem to register with the material.

Do I need to do anything other than updating the uniform, like calling needsUpdate or something like that?

(The value of time is changing, I checked that ๐Ÿ˜‰)

Hereโ€™s an abbreviated version of what Iโ€™m doing:

const fragmentShader = `
precision highp float; 
uniform float u_time;

void main() {
    gl_FragColor = vec4(u_time, u_time, u_time, 1.0);
}
`;

โ€ฆ

const uniforms = {
    u_time: {type: 'f', value: time},
};

โ€ฆ

<mesh rotation-y={time}>
    <planeBufferGeometry name="geometry" args={[4, 4]} />
    <shaderMaterial
    name="material"
    uniforms={uniforms}
    vertexShader={vertexShader}
    fragmentShader={fragmentShader}
</mesh>

Hereโ€™s a Code Sandbox demonstrating the problem: https://codesandbox.io/embed/rw980y5jnq

I looked at the documentation and the issues, but couldnโ€™t find a solution.

All examples with updating shaders seem to use react-spring and <animated.shaderMaterial> โ€“ does that do anything special?

I also tried using a ref on the mesh and useRender, like so, which didnโ€™t work, either:

useRender(() => {
    if (
      mesh.current &&
      mesh.current.material &&
      mesh.current.material.uniforms
    ) {
      mesh.current.material.uniforms.u_time.value = time;
    }
  });

useRender not called

Hi, ๐Ÿ‘‹

It seems hook useRender is not called for each frame, like doc suggest:
useRender(() => console.log('new frame'));

This code takes place in a sub-component _3DObjects of Canvas component. But it does not log 'new frame', unless the _3DObjects has some modified props.

Is it the normal behavior ? I thought it would be called for each frame, independentely of React component re-render.

Creating a basic shape and extruding

Just trying to learn how to create and extrude shapes from the three.js example.
How would this look in react-three-fibre?
https://threejs.org/docs/index.html#api/en/extras/core/Shape

Three.js code

var heartShape = new THREE.Shape();

heartShape.moveTo( 25, 25 );
heartShape.bezierCurveTo( 25, 25, 20, 0, 0, 0 );
heartShape.bezierCurveTo( 30, 0, 30, 35,30,35 );
heartShape.bezierCurveTo( 30, 55, 10, 77, 25, 95 );
heartShape.bezierCurveTo( 60, 77, 80, 55, 80, 35 );
heartShape.bezierCurveTo( 80, 35, 80, 0, 50, 0 );
heartShape.bezierCurveTo( 35, 0, 25, 25, 25, 25 );

var extrudeSettings = { amount: 8, bevelEnabled: true, bevelSegments: 2, steps: 2, bevelSize: 1, bevelThickness: 1 };

var geometry = new THREE.ExtrudeGeometry( heartShape, extrudeSettings );

var mesh = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial() );

Suggestion: Don't create default camera, scene, and render loop

I don't think this library should hard code a scene, camera, or render loop, for example hard coding the default camera to a perspectivecamera at position 0,0,5. I think this library should be more general than that. A Three setup can have 0 to many cameras of different types, 0 to many scenes, and itself doesn't start a requestAnimationFrame loop. These seem anti-React too, to hard code components into a library.

incorrect unsubscribe logic?

src/canvas.js, was I missing the point?

subscribe: (fn, main) => {
   state.current.subscribers.push(fn)
   return () => (state.current.subscribers = state.current.subscribers.filter(s => s === fn))
},

Examples don't build

Steps:

  1. git clone https://github.com/drcmda/react-three-fiber.git
  2. cd react-three-fiber/examples
  3. yarn or npm install
  4. yarn start or npm start

Expected: Successful webpack output.

Actual

โœ– ๏ฝขwdm๏ฝฃ:
ERROR in ./components/Tina.js
Module not found: Error: Can't resolve 'lodash-es/flatten' in '/home/tstelzer/dev/source/react-three-fiber/examples/components'
 @ ./components/Tina.js 5:0-37 24:51-55
 @ ./index.js
 @ multi (webpack)-dev-server/client?http://localhost:8080 index.js

ERROR in ./components/Tina.js
Module not found: Error: Can't resolve 'three/src/Three' in '/home/tstelzer/dev/source/react-three-fiber/examples/components'
 @ ./components/Tina.js 1:0-41 10:12-22 89:10-26
 @ ./index.js
 @ multi (webpack)-dev-server/client?http://localhost:8080 index.js

ERROR in ./node_modules/react-spring/three.js
Module not found: Error: Can't resolve 'three/src/Three' in '/home/tstelzer/dev/source/react-three-fiber/examples/node_modules/react-spring'
 @ ./node_modules/react-spring/three.js 1:0-41 1827:31-36
 @ ./components/Tina.js
 @ ./index.js
 @ multi (webpack)-dev-server/client?http://localhost:8080 index.js

ERROR in ./resources/loaders/SVGLoader.js
Module not found: Error: Can't resolve 'three/src/Three' in '/home/tstelzer/dev/source/react-three-fiber/examples/resources/loaders'
 @ ./resources/loaders/SVGLoader.js 1:0-112 9:51-72 16:21-31 96:21-30 98:22-29 99:24-31 100:27-34 482:21-30 519:21-30 542:21-30 554:24-28 556:21-30 567:24-28 569:21-30 580:21-30 646:26-33 749:23-30 750:23-30 806:29-36 807:29-36 808:29-36 809:29-36 810:31-38
 @ ./components/Tina.js
 @ ./index.js
 @ multi (webpack)-dev-server/client?http://localhost:8080 index.js

ERROR in ../src/canvas.js
Module not found: Error: Can't resolve 'three/src/Three' in '/home/tstelzer/dev/source/react-three-fiber/src'
 @ ../src/canvas.js 3:0-41 40:41-56 41:37-50 43:20-43 60:28-41 62:18-28 99:27-46 108:30-41
 @ ../index.js
 @ ./components/Tina.js
 @ ./index.js
 @ multi (webpack)-dev-server/client?http://localhost:8080 index.js

ERROR in ../src/reconciler.js
Module not found: Error: Can't resolve 'three/src/Three' in '/home/tstelzer/dev/source/react-three-fiber/src'
 @ ../src/reconciler.js 1:0-41 85:38-43
 @ ../index.js
 @ ./components/Tina.js
 @ ./index.js
 @ multi (webpack)-dev-server/client?http://localhost:8080 index.js
โ„น ๏ฝขwdm๏ฝฃ: Failed to compile.

Vertices not updating

Hi, ๐Ÿ‘‹

first of all I really enjoy playing around with react-three-fiber and I've even started to write my own thin wrapper around another library using react-three-fiber's reconciler ๐ŸŽ‰.

While playing around with react-three-fiber I've found that my vertices are not updating, that might be an Three.js issue.

Here's a quick example, the button 'Add vertex' dynamically adds a new Vertex on each click, and applyProps gets properly called, but in the end the vertices are not updated: https://codesandbox.io/s/qqq7qpqvo9

Crop objects on Resize, rather than Scale

When the canvas is scaled in rtf it scales all the objects inside it to fit the new dimensions.
I would prefer it to crop the display rather than scale it. Is it possible to get more control over the resize handler?

Can't use non-three.js elements like <div> inside a <Canvas>?

Again i'm a reconciler newcomer, but these lines look suspect in the createInstance method:

https://github.com/drcmda/react-three-fiber/blob/master/src/reconciler.js#L129-L130

    const target = catalogue[name] || THREE[name]
    instance = Array.isArray(args) ? new target(...args) : new target(args)

Two things seem to happen. First, the error itself seems to be getting eaten somewhere, but I'm not sure where. All that's logged is

The above error occurred in the <ul> component:
    in ul (created by SelectableMenu)

But the error itself seems to be logged after that statement:

Uncaught TypeError: target is not a constructor

This error itself isn't this most obvious to debug.

Why would I want to put a <div> inside a <Canvas> you ask? I'm using react-portal to make my 3d menu element also be able to render a DOM element, and have both the 3d element and GUI element use the same props. It's pretty convenient.

That doesn't seem compatible with this library. Do you have a suggestion for another approach?

Maybe a memory leak on SVG parallax example

Hi, ๐Ÿ‘‹

I notice steady GPU memory footprint climb on SVG parallax example. I don't notice same case on others example.

Maybe related to effect that need to be cleaned up or undisposed Scene when mount/unmount. Still need further profiling.

For setInterval effect clean up:

useEffect(() => {
        const interval = setInterval(() => setPage(i => (i + 1) % svgs.length), 4000)
        return () => clearInterval(interval)
}, [])

Multiple canvases display the same thing

Hey again, so I'm still trying to replicate this example. From their code, a canvas is created for each couple of images.

Here is my attempt, also based on the sandbox you provided me in issue #23 (thanks again!)
https://codesandbox.io/s/w5902o607

I'm not sure if it's updating to v1.3.7 that created this but now all canvases are showing the same image, and behave like one. I had it running properly at one point (under v1.3.6) but then I was running into THREE.WebGLRenderer: Context Lost issues.

Maybe this is not the right place to ask, but it really felt related to the version bump from v1.3.6 to v1.3.7

TypeScript

Any interest in supporting TypeScript?
Initially probably only the inner workings (react-reconciler provides types) and then finding a way to generate types for the THREE bindings.

Animating uniforms in ShaderMaterial

Hey, I'm quite new to all this, but I thought this might be a good occasion to get more familiar with THREE. I'm trying to replicate this demo here using react-three-fiber. https://tympanus.net/Development/DistortionHoverEffect/

From the demo source the material is built and animated as below with GSAP (dispFactor value animates from 0 to 1):

var mat = new THREE.ShaderMaterial({
    uniforms: {
        effectFactor: { type: "f", value: intensity },
        dispFactor: { type: "f", value: 0.0 },
        texture: { type: "t", value: texture1 },
        texture2: { type: "t", value: texture2 },
        disp: { type: "t", value: disp }
    },

    vertexShader: vertex,
    fragmentShader: fragment,
    transparent: true,
    opacity: 1.0
});

// animation with GSAP
parent.addEventListener(evtIn, function(e) {
    TweenMax.to(mat.uniforms.dispFactor, speedIn, {
        value: 1,
        ease: easing
    });
});

I've tried to do the same with react-three-fiber and react-spring but nothing happens or nothing shows. Any idea what I'm doing wrong?

const [hovered, setHover] = useState(false)
const hover = useCallback(() => setHover(true), [])
const unhover = useCallback(() => setHover(false), [])
const { progress } = useSpring({ progress: hovered ? 1 : 0 })

return (
    <anim.mesh {...props} onHover={hover} onUnhover={unhover}>
      <planeBufferGeometry name="geometry" args={[3.8, 3.8]} />
      <anim.shaderMaterial
        name="material"
        uniforms={{
          effectFactor: { type: 'f', value: intensity },
          dispFactor: { type: 'f', value: progress },
          texture: { type: 't', value: texture1 },
          texture2: { type: 't', value: texture2 },
          disp: { type: 't', value: displacement }
        }}
        {...HoverShader}
        transparent
        opacity={1}
      />
    </anim.mesh>
);

More interaction events / react-with-gesture

Looks like I'll need some more mouse events, eg. up, down, move, wheel etc.
I might see if I can do a PR to implement them but it looks like you have an interface with react-with-gesture working. Do you have an example of that I can play with?

Canvas element continuously grows in size

Hi, I recently tried to incorporate a Canvas object into a simple create-react-app project and I'm getting to some strange behavior.

The canvas element continuously grows without bound.

Versions :
Chrome - Version 70.0.3538.67
"react": "^16.8.4",
"react-dom": "^16.8.4",
"react-scripts": "2.1.8",
"react-three-fiber": "^1.3.8",
"three": "^0.102.1"

Repro :

  1. create-react-app
  2. npm install react-three-fiber
  3. npm install three
  4. Replace app.js with the following
import React, { Component } from 'react';
import { Canvas } from 'react-three-fiber'
import * as THREE from 'three';

function Thing({ vertices, color }) {
  return (
    <group ref={ref => console.log('we have access to the instance')}>
      <line>
        <geometry
          name="geometry"
          vertices={vertices.map(v => new THREE.Vector3(...v))}
          onUpdate={self => (self.verticesNeedUpdate = true)}
        />
        <lineBasicMaterial name="material" color="black" />
      </line>
      <mesh
        onClick={e => console.log('click')}
        onHover={e => console.log('hover')}
        onUnhover={e => console.log('unhover')}>
        <octahedronGeometry name="geometry" />
        <meshBasicMaterial name="material" color="peachpuff" opacity={0.5} transparent />
      </mesh>
    </group>
  )
}



class App extends Component {
  render() {
    return (
      <Canvas>
        <Thing vertices={[[-1, 0, 0], [0, 1, 0], [1, 0, 0], [0, -1, 0], [-1, 0, 0]]} />
      </Canvas>

    );
  }
}

export default App;
  1. npm run start

Watch the canvas element grow in size.

addAttribute

Hi, ๐Ÿ‘‹

if this is about a bug, before you go ahead, please do us a favour and make sure to check the threejs issue tracker for the problem you are experiencing. This library is just a soft wrap around threejs without direct dependencies. So if something is flipped upside down, or doesn't project the way you intent to, there's a good chance others will have experienced the same issue with plain threejs.

Setting Renderer Properties

I'm just trying to turn on anti aliasing. This is how it's done in three.js:
var renderer = new THREE.WebGLRenderer( { antialias: true } );

From what I can tell Canvas basically acts as the renderer in react-three-fibre. Is that correct?
I've tried the following but it doesn't appear to work:
<Canvas camera={{ position: [0, 0, 400] }} antialias={true}>

Animation - React-Spring / Use-Gesture Example

Hey Paul, do you have a working example of this react-spring / use-gesture sandbox for me to play with? https://codesandbox.io/s/l4vxzx5kjq - I'm not 100% sure why it's not working.

I'm ending up with code like the following to optimise the user changing the shape of 1000s objects at once onWheel. But the user may only perform this action a small number of times and I'm conscious of the fact that the code is getting executed on every render. I'm thinking react-spring will be a smarter approach.

export const BarMesh = ({ bar, ...props }) => {
    const geometryRef = useRef()
    useRender(() => {
        const geometry = geometryRef.current      
        // bar.vertices is memoized
        geometry.addAttribute('position', bar.vertices);
        geometry.attributes.position.needsUpdate = true
        geometry.computeBoundingSphere()
        geometry.computeBoundingBox()
        geometry.computeVertexNormals()
    });
    return (
        <mesh {...props} >
            <bufferGeometry ref={geometryRef} attach="geometry" />
            <meshNormalMaterial attach="material"/>
        </mesh>
    )
}

Suggestion for imperative update api

I'm finding this to be a very common pattern I'm using to avoid having react-three-fiber recreate new geometry for objects I need to regularly update. I'm wondering if there's a nicer api for this.

Let's say I'm creating a dynamic cube. I regularly do the following:

  • I will create store with its dimensions
  • create geometry and mesh instance, cached in the store
  • have a memoised / reactive function that updates the geometry / mesh based off the cube dimensions
  • apply the updated mesh to the primitive tag
class CubeStore {
    @observable x = 0
    @observable y = 0
    @observable z = 0

    constructor(x,y,z) {
        this.x = x
        this.y = y
        this.z = z
        this._geometry = getCubeGeometry(x,y,z)
    }

    @computed get geometry() {
         const geometry = this._geometry
         geometry.addAttribute('position', getCubeVertices(this.x, this.y, this.z))
         geometry.attributes.position.needsUpdate = true
         geometry.computeBoundingSphere()
         return geometry
     }
}

const geometry = ({cube}) => {
    return <primitive object={cube.geometry} />
}

I'm ending up with a lot of code in my stores which I feel should be in the component. And I know you can't avoid creating new instances because of the nature of three.js. But what I'm thinking is maybe if the component could have an update function, which react-three-fiber could use instead of reinstantiating the object on render.

Something like this would keep that logic inside the component and feels a little nicer:

class CubeStore {
    @observable x = 0
    @observable y = 0
    @observable z = 0

    constructor(x,y,z) {
        this.x = x
        this.y = y
        this.z = z
    }
}

const geometry = ({cube}) => {
    ref = useRef()
    // use this on update instead of creating a new geometry instance
    useImperativeUpdate( 
        () => {
             const geometry = ref.current
             geometry.addAttribute('position', getCubeVertices(cube.x, cube.y, cube.z))
             geometry.attributes.position.needsUpdate = true
             geometry.computeBoundingSphere()
        }, 
        [cube.x, cube.y, cube.z] // execute only if these change
    )
    return <bufferGeometry ref={ref} ... />
}

What's your thoughts on something like that?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.