Rajan Randev
Rajan Randev

Reputation: 39

Converting Three.js from vanilla JavaScript to React Three Fiber

I've been trying to convert three.js written in vanilla JS to React Three fiber.

import * as THREE from 'three';

let scene, camera, renderer;

//Canvas
const canvas = document.querySelector('canvas')

//Number of line particles
let lineCount = 6000;

//adding buffer Geometry
let geom = new THREE.BufferGeometry();

//Giving the Buffer Geometry attributes
geom.setAttribute('position', new THREE.BufferAttribute(new Float32Array(6 * lineCount), 3));
geom.setAttribute('velocity', new THREE.BufferAttribute(new Float32Array(2 * lineCount), 1));

//creating array variable for the position
let pos = geom.getAttribute('position');
let posArray = pos.array;

//creating array variable for the velocity
let vel = geom.getAttribute('velocity');
let velArray = vel.array;

//function to initiate
const init = () => {
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 1, 500);
  camera.position.z = 200;

  renderer = new THREE.WebGLRenderer({antialias: true, canvas: canvas});
  renderer.setSize(window.innerWidth, window.innerHeight);

  for (let lineIndex = 0; lineIndex < lineCount; lineIndex++){
    let x = Math.random() * 400 - 200;
    let y = Math.random() * 200 - 100;
    let z = Math.random() * 500 - 100;
    let xx = x;
    let yy = y;
    let zz = z;

    //line starting position
    posArray[6 * lineIndex] = x;
    posArray[6 * lineIndex + 1] = y;
    posArray[6 * lineIndex + 2] = z;

    //line ending position
    posArray[6 * lineIndex + 3] = xx;
    posArray[6 * lineIndex + 4] = yy;
    posArray[6 * lineIndex + 5] = zz;

    velArray[2 * lineIndex] = velArray[2 * lineIndex + 1] = 0;
  }

  let lineMat = new THREE.LineBasicMaterial({color: '#ffffff'});
  let lines = new THREE.LineSegments(geom, lineMat);
  scene.add(lines);

  window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth/ window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  }, false);
  animate();
}

const animate = () => {
  for (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {
    velArray[2 * lineIndex] += 0.03;
    velArray[2 * lineIndex + 1] += 0.025;

    posArray[6 * lineIndex + 2] += velArray[2 * lineIndex];
    posArray[6 * lineIndex + 5] += velArray[2 * lineIndex + 1];

    if (posArray[6 * lineIndex + 5] > 200) {
      let z = Math.random() * 200 - 100;
      posArray[6 * lineIndex + 2] = z;
      posArray[6 * lineIndex + 5] = z;
      velArray[2 * lineIndex] = 0;
      velArray[2 * lineIndex + 1] = 0;
    }
  }

  pos.needsUpdate = true;
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

init();

I'm having difficulty in converting the init and animate functions.

This is what I have so far, I've added the positions and velocity as bufferAttributes and specified the starting and ending coordinates in the for loop:

const StarLine = () => {

    const warpFieldMesh = useRef();
    const bufAtPos = useRef();
    const bufAtVel = useRef();

    const count = 100;
  
      const [positions, velocity] = useMemo(() => {
  
        let positions = []
        let velocity = []
  
        for (let lineIndex = 0; lineIndex < count; lineIndex++){
            let x = Math.random() * 400 - 200;
            let y = Math.random() * 200 - 100;
            let z = Math.random() * 500 - 100;
            let xx = x;
            let yy = y;
            let zz = z;
        
            //line starting position
            positions[6 * lineIndex] = x;
            positions[6 * lineIndex + 1] = y;
            positions[6 * lineIndex + 2] = z;
        
            //line ending position
            positions[6 * lineIndex + 3] = xx;
            positions[6 * lineIndex + 4] = yy;
            positions[6 * lineIndex + 5] = zz;
        
            velocity[2 * lineIndex] = velocity[2 * lineIndex + 1] = 0;
          }
          
        return [new Float32Array(positions), new Float32Array(velocity)]

      }, [])

      useFrame(() => {
          
      })
  
      return (

        <line ref={warpFieldMesh}>

          <bufferGeometry attach="geometry">

            <bufferAttribute 
                ref={bufAtPos}
                attachObject={["attributes", "position"]} 
                count={positions.length / 3} 
                array={positions} 
                itemSize={3} 
                />
            <bufferAttribute 
                ref={bufAtVel}
                attachObject={["attributes", "velocity"]} 
                count={velocity.length / 2}  
                array={velocity} 
                itemSize={1} 
                />

          </bufferGeometry>

          <lineBasicMaterial
            attach="material" 
            color={'#ffffff'}
            />

        </line>

      )
}

The vanilla JavaScript produces:

enter image description here

Upvotes: 0

Views: 1032

Answers (2)

ANKIT NAYAK
ANKIT NAYAK

Reputation: 186

I fixed this 😊, and it's working perfectly now -

import React, { useRef, useEffect } from 'react';
import * as THREE from 'three';

const ThreeSTAR = () => {
const warpFieldMesh = useRef();
const lineCount = 1000;

useEffect(() => {
    let scene, camera, renderer;
    const canvas = warpFieldMesh.current;

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.z = 200;

    renderer = new THREE.WebGLRenderer({ antialias: true, canvas });
    renderer.setSize(window.innerWidth, window.innerHeight);

    const geom = new THREE.BufferGeometry();
    geom.setAttribute('position', new THREE.BufferAttribute(new Float32Array(6 * lineCount), 3));
    geom.setAttribute('velocity', new THREE.BufferAttribute(new Float32Array(2 * lineCount), 1));

    const pos = geom.getAttribute('position');
    const posArray = pos.array;
    const vel = geom.getAttribute('velocity');
    const velArray = vel.array;

    for (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {
        let x = Math.random() * 400 - 200;
        let y = Math.random() * 200 - 100;
        let z = Math.random() * 500 - 100;
        let xx = x;
        let yy = y;
        let zz = z;

        posArray[6 * lineIndex] = x;
        posArray[6 * lineIndex + 1] = y;
        posArray[6 * lineIndex + 2] = z;

        posArray[6 * lineIndex + 3] = xx;
        posArray[6 * lineIndex + 4] = yy;
        posArray[6 * lineIndex + 5] = zz;

        velArray[2 * lineIndex] = velArray[2 * lineIndex + 1] = 0;
    }

    const lineMat = new THREE.LineBasicMaterial({ color: '#ffffff' });
    const lines = new THREE.LineSegments(geom, lineMat);
    scene.add(lines);

    const resizeHandler = () => {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    };

    window.addEventListener('resize', resizeHandler, false);

    const animate = () => {
        for (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {
            velArray[2 * lineIndex] += 0.03;
            velArray[2 * lineIndex + 1] += 0.025;

            posArray[6 * lineIndex + 2] += velArray[2 * lineIndex];
            posArray[6 * lineIndex + 5] += velArray[2 * lineIndex + 1];

            if (posArray[6 * lineIndex + 5] > 200) {
                let z = Math.random() * 200 - 100;
                posArray[6 * lineIndex + 2] = z;
                posArray[6 * lineIndex + 5] = z;
                velArray[2 * lineIndex] = 0;
                velArray[2 * lineIndex + 1] = 0;
            }
        }

        pos.needsUpdate = true;
        renderer.render(scene, camera);
        requestAnimationFrame(animate);
    };

    animate();

    return () => {
        window.removeEventListener('resize', resizeHandler);
    };
}, []);

useEffect(() => {
    document.body.style.overflow = 'hidden';

    return () => {
        document.body.style.overflow = 'visible';
    };
}, []);

return <canvas ref={warpFieldMesh} />;
};

export default ThreeSTAR;

make sure you warp this using div not canvas in app.jsx

import React from 'react';
import './App.css';
import ThreeSTAR from './components/threeSTAR';

function App() {
return (
<div>
  <ThreeSTAR />
</div>
);
}

export default App;

Upvotes: 1

Epiczzor
Epiczzor

Reputation: 427

The stuff in your animate function can go in the useFrame hook, its a r3f hook that is called every frame

Upvotes: 2

Related Questions