pmndrs / drei

🥉 useful helpers for react-three-fiber
https://docs.pmnd.rs/drei
MIT License
8.25k stars 680 forks source link

[help] Implementing record GIF/video functionality #84

Closed akella closed 4 years ago

akella commented 4 years ago

Hi! So, everybody who develops r3f things wants to share videos, but its either screen-grabs or quicktime, which is not good, and hard to loop. So i thought it might be a great idea to do a GIF recorder with r3f. Or, lets say, generative art tool.

I did this with ccapture, which is working, but im not sure how to better implement that all yet. There are 3 things to improve, which im having hard times yet, but i decided i create this issue, and see if someone wants this kind of tool at all :)

1) For now ccapture is not npm ready, so to use it, i took one from this branch https://github.com/spite/ccapture.js/pull/89 and used it locally like this: https://github.com/spite/ccapture.js/blob/c58ac104dec9b8e5d199249f73ff4580f5b90e61/examples/bundler/README.md But @thespite said recently he is updating module. MB we could gently ask him to finish that update.😅 Also, mb someone knows simple modules based on https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder? 2) Make playhead accessible from components, usually 0..1 range value, used to make loop, usePlayhead mb? Will something like this work?

import { useThree } from 'react-three-fiber'
export function usePlayhead( duration) {
  const { clock } = useThree()
  return [clock.elapsedTime%duration]
}

3) a bit offtopic: Add dat.gui/whatever controls, as another module in drei probably, that will make a powerful generative tool.

here is current code of the "tool", its really simple, and mb you can find that useful.

import ReactDOM from 'react-dom'
import React, { useRef, useState, Suspense } from 'react'
import { Canvas, useFrame, extend, useThree} from 'react-three-fiber'
import ccapture from "ccapture.js";

const SETTINGS = {
  duration: 2,
}
const capturer = new CCapture({
  format: "webm",
  framerate: 25,
  verbose: true,
  // motionBlurFrames: 6
});

function Recorder() {
  let shouldRecord = false;
  let isRecording = false;
  let prevPlayhead = 0;
  document.onkeyup = function(e) {
    if (e.which == 82) { // press R to start recording 
      alert("recording now");
      shouldRecord = true;
    } 
  };

  useFrame((state) => {
    let currentPlayhead = state.clock.getElapsedTime()%SETTINGS.duration;

    if(isRecording && currentPlayhead<prevPlayhead){
      shouldRecord = false;
      isRecording = false;
      capturer.stop();
      capturer.save();
    }

    if(!isRecording && shouldRecord && currentPlayhead<prevPlayhead){
      isRecording = true;
      capturer.start();
    }

    if(isRecording){
      capturer.capture(state.gl.domElement)
    }

    prevPlayhead = currentPlayhead
  })
  return (
    <group dispose={null}>
    </group>
  )
}

function Box(props) {
  const ref = useRef()
  useFrame(() => (ref.current.rotation.x = ref.current.rotation.y += 0.01))

  return (
      <mesh 
        ref={ref} 
        {...props}
        >
        <boxBufferGeometry attach="geometry" args={[1,1,1]} />
        <meshBasicMaterial attach="material" color={'hotpink'} />
      </mesh>
    )
}

ReactDOM.render(
  <Canvas colorManagement>
    <Box />
    <Recorder />
  </Canvas>,
  document.getElementById('root')
)
gsimone commented 4 years ago

Quick answer for point 3 while I think about the other points: we tend to use react-three-gui , example here: https://r3f-control.netlify.app/?ctrl ( code https://github.com/gsimone/r3f-control )

gsimone commented 4 years ago

This is now being worked on here https://github.com/gsimone/use-capture