From threejs to R3F: setXYZ bufferAttribute

Hello!

I try to use a conveyor in my simulation. I found this code that fits quite well with what I want, except that I work with R3F. I tried to convert this code, but I can’t get the bufferAttribute part to work.

I have pasted below two of my attempts. With this code, I can get a flat plane with the texture moving forward, but I can’t get the curvature and the second plane.

import * as THREE from "three";
import { useFrame } from "react-three-fiber";
import React, { useRef } from "react";

function createTexture() {
  var c = document.createElement("canvas");
  c.width = c.height = 256;
  var ctx = c.getContext("2d");
  ctx.fillStyle = "maroon";
  ctx.fillRect(0, 0, c.width, c.height);
  ctx.strokeStyle = "white";
  ctx.beginPath();
  ctx.moveTo(0, 0);
  ctx.lineWidth = 100;
  ctx.lineTo(c.width, c.height);
  ctx.stroke();

  var texture = new THREE.CanvasTexture(c);
  texture.wrapS = THREE.RepeatWrapping;
  texture.wrapT = THREE.MirroredRepeatWrapping;
  texture.repeat.set(10, 2);

  return texture;
}
var clock = new THREE.Clock();

var path = new THREE.Path();
path.absarc(5, 0, 1, Math.PI * 0.5, Math.PI * 1.5, true);
path.absarc(-5, 0, 1, Math.PI * 1.5, Math.PI * 0.5, true);
path.closePath();
console.log(path);
var basePts = path.getSpacedPoints(200).reverse();

// first try not working
export function ConveyorBelt() {
  var baseConv = [];
  basePts.forEach((d) => {
    baseConv.push(d.x, d.y, -2);
  });
  basePts.forEach((d) => {
    baseConv.push(d.x, d.y, 2);
  });
  const MBasRef = useRef();
  useFrame(() => {
    MBasRef.current.map.offset.x = clock.getElapsedTime();
  });
  console.log(baseConv);
  return (
    <mesh>
      <planeBufferGeometry attach="geometry">
        <bufferAttribute
          attachObject={["attributes", "position"]}
          count={baseConv.length / 3}
          array={baseConv}
          itemSize={3}
        />
      </planeBufferGeometry>
      <meshBasicMaterial
        ref={MBasRef}
        side={THREE.DoubleSide}
        map={createTexture()}
      />
    </mesh>
  );
}


// second try still not working...
export function ConveyorBelt() {
  const MBasRef = useRef();
  useFrame(() => {
    MBasRef.current.map.offset.x = clock.getElapsedTime();
  });

  return (
    <mesh>
      <planeBufferGeometry attach="geometry">
        {basePts.forEach(
          (d, idx) => (
            (
              <bufferAttribute
                attachObject={["attributes", "position"]}
                args={([d.x, d.y, -2], idx)}
              />
            ),
            (
              <bufferAttribute
                attachObject={["attributes", "position"]}
                args={([d.x, d.y, 2], idx + 201)}
              />
            )
          )
        )}
      </planeBufferGeometry>
      <meshBasicMaterial
        ref={MBasRef}
        side={THREE.DoubleSide}
        map={createTexture()}
      />
    </mesh>
  );
}

these are side effects, in react that’s always useLayoutEffect

const ref = useRef()

useLayoutEffect(() => {
  ref.current.geometry.attributes.position.setXYZ(idx, p.x, p.y, -2)
}, [])

return (
  <mesh ref={ref}>
    ...

layout effects fire after react has created the real threejs objects, but before they are committed on screen or into their parents.

that horrible attachObject={[“foo”, “bar”]} syntax will go away soon, it will be like this fragrant-breeze-ev4w4 - CodeSandbox

Thank you very much! this part works now, but I have still a problem.

In line 21 of the original code, there is this part.
let g = new THREE.PlaneGeometry(1, 1, 200, 1);
widthSegments is set to 200, the value of the others keeps their default value.
I tried to set in my code like follow
or also
But this does not work, it seems to have no effect
I also tried to use but I can’t make it work with the other part of the code. Moreover “planeGeometry” is slightly less performant if I understood well.

new THREE.PlaneGeometry(1, 1, 200, 1)

is the same as

<planeGeometry args={[1, 1, 200, 1]} />

as for performance i don’t know what you mean. there is no difference between jsx and vanilla, it’s the same exact thing in the end, just a different notation.

1 Like

Sorry I was talking about “planeBufferGeometry” and not “planeGeometry”
But your solution worked anyway. Thank you!

pBG and pG are the same thing. pG is the correct spelling afaik while the other is deprecated. geometry was removed from threejs, now there is only buffergeometry.

1 Like