Skip to content

Instantly share code, notes, and snippets.

@JanHmmr
Created April 24, 2024 20:15
Show Gist options
  • Select an option

  • Save JanHmmr/810bc3f58b44801ea5a18025f3cc209b to your computer and use it in GitHub Desktop.

Select an option

Save JanHmmr/810bc3f58b44801ea5a18025f3cc209b to your computer and use it in GitHub Desktop.
realtime from 3d old fal model
import React, { useContext, useState, useEffect, useRef } from 'react';
import SidebarLayout from '@/components/layout';
import { LuLock } from "react-icons/lu";
import { PageContext } from '@/utils/contexts';
import * as fal from "@fal-ai/serverless-client";
import Image from 'next/image';
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
fal.config({
proxyUrl: "/api/fal/proxy",
});
const seed = Math.floor(Math.random() * 100000);
const baseArgs = {
sync_mode: true,
strength: 0.3,
seed,
};
const TextureGeneration = () => {
const { setauthToken, setloginModal, setupgradeModal } = useContext(PageContext);
const [input, setInput] = useState('');
const [image, setImage] = useState(null);
const [localImage, setLocalImage] = useState(null);
const [isClient, setIsClient] = useState(false);
const [modelSelectOpen, setModelSelectOpen] = useState(true);
const [models, setModels] = useState([
{ name: 'Model1.glb', image: '/Example1.png' },
{ name: 'Model2.glb', image: '/Example2.png' },
{ name: 'Model3.glb', image: '/Example3.png' },
{ name: 'Model4.glb', image: '/Example4.png' },
{ name: 'Model5.glb', image: '/Example5.png' },
{ name: 'Model6.glb', image: '/Example6.png' },
{ name: 'Model7.glb', image: '/Example7.png' },
{ name: 'Model8.glb', image: '/Example8.png' },
]);
const [selectedModel, setSelectedModel] = useState(null);
const canvasRef = useRef(null);
const rendererRef = useRef(null);
const sceneRef = useRef(null);
const cameraRef = useRef(null);
const controlsRef = useRef(null);
const modelRef = useRef(null);
useEffect(() => {
setIsClient(true);
}, []);
useEffect(() => {
if (isClient) {
const canvas = canvasRef.current;
const renderer = new THREE.WebGLRenderer({ canvas, preserveDrawingBuffer: true });
rendererRef.current = renderer;
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
sceneRef.current = scene;
const camera = new THREE.PerspectiveCamera(75, canvas.clientWidth / canvas.clientHeight, 0.1, 1000);
camera.position.z = 5;
cameraRef.current = camera;
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 1;
controls.maxDistance = 500;
controlsRef.current = controls;
// Add strong lighting
const directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.position.set(1, 1, 1);
scene.add(directionalLight);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
scene.add(ambientLight);
const pointLight1 = new THREE.PointLight(0xffffff, 1.5);
pointLight1.position.set(5, 5, 5);
scene.add(pointLight1);
const pointLight2 = new THREE.PointLight(0xffffff, 1.5);
pointLight2.position.set(-5, 5, -5);
scene.add(pointLight2);
const animate = () => {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
};
animate();
return () => {
renderer.dispose();
};
}
}, [isClient]);
useEffect(() => {
if (selectedModel) {
const loader = new GLTFLoader();
loader.load(selectedModel.name, (gltf) => {
const model = gltf.scene;
sceneRef.current.add(model);
modelRef.current = model;
});
}
}, [selectedModel]);
const { send } = fal.realtime.connect('110602490-sdxl-turbo-realtime', {
connectionKey: 'realtime-3daistudio-app',
clientOnly: true,
onResult(result) {
if (result.error) return;
setImage(result.images[0].url);
},
});
async function getDataUrl() {
const canvas = canvasRef.current;
const renderer = rendererRef.current;
renderer.render(sceneRef.current, cameraRef.current);
return canvas.toDataURL('image/png');
}
useEffect(() => {
let timeoutId;
const handleChange = async () => {
let dataUrl = await getDataUrl();
setLocalImage(dataUrl);
if (dataUrl !== localImage) {
send({
...baseArgs,
image_url: dataUrl,
prompt: input,
});
}
};
const debouncedHandleChange = () => {
clearTimeout(timeoutId);
timeoutId = setTimeout(handleChange, 300);
};
if (isClient && controlsRef.current) {
controlsRef.current.addEventListener('change', debouncedHandleChange);
}
return () => {
if (isClient && controlsRef.current) {
controlsRef.current.removeEventListener('change', debouncedHandleChange);
}
};
}, [isClient, input, send, localImage]);
const handleFileUpload = (event) => {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = (event) => {
const arrayBuffer = event.target.result;
const loader = new GLTFLoader();
const scene = sceneRef.current;
// Remove the existing model from the scene
if (modelRef.current) {
scene.remove(modelRef.current);
}
// Load the uploaded GLB model
loader.parse(arrayBuffer, '', (gltf) => {
const model = gltf.scene;
scene.add(model);
modelRef.current = model;
setModelSelectOpen(false);
});
};
reader.readAsArrayBuffer(file);
};
const handleModelSelect = (model) => {
setSelectedModel(model);
setModelSelectOpen(false);
};
return (
<SidebarLayout>
<div className="relative w-full h-full flex flex-col items-center justify-center bg-[#1f1f1f] text-white">
<h1 className="text-4xl font-bold mb-8">Realtime Texture Generation</h1>
<div className="flex space-x-8">
<div className="flex flex-col items-center">
<div className="w-[550px] h-[550px] border-2 border-gray-500 mb-4">
<canvas ref={canvasRef} width={550} height={550} />
</div>
<input
type="file"
accept=".glb"
onChange={handleFileUpload}
className="px-4 py-2 bg-blue-500 text-white rounded-lg cursor-pointer"
/>
</div>
<div className="flex flex-col items-center">
<div className="w-[550px] h-[550px] border-2 border-gray-500 flex items-center justify-center">
{image ? (
<Image src={image} width={550} height={550} alt="fal image" />
) : (
<div className="animate-spin rounded-full h-20 w-20 border-t-4 border-white"></div>
)}
</div>
<input
className="px-4 py-2 border border-gray-500 bg-transparent rounded-lg w-full mt-4"
value={input}
onChange={(e) => {
setInput(e.target.value);
}}
placeholder="Enter prompt"
/>
</div>
</div>
</div>
{modelSelectOpen && (
<div className="fixed inset-0 flex items-center justify-center z-50 bg-black bg-opacity-50">
<div className="bg-white p-8 rounded shadow-lg w-[600px]">
<h2 className="text-2xl font-bold mb-4 text-gray-900">Select Model</h2>
<div className="grid grid-cols-4 gap-4">
{models.map((model, index) => (
<div
key={index}
className="cursor-pointer border border-gray-300 p-2 rounded"
onClick={() => handleModelSelect(model)}
>
<Image src={model.image} width={200} height={200} alt={model.name} />
<p className="text-center">{model.name}</p>
</div>
))}
</div>
<div className="mt-8 flex justify-between">
<button
className="px-4 py-2 bg-gray-200 text-gray-700 rounded"
onClick={() => setModelSelectOpen(false)}
>
Cancel
</button>
<input
type="file"
accept=".glb"
onChange={handleFileUpload}
className="px-4 py-2 bg-blue-500 text-white rounded cursor-pointer"
/>
</div>
</div>
</div>
)}
</SidebarLayout>
);
};
export default TextureGeneration;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment