OBJ Model not loading in react three fiber - reactjs

I'm trying to add an OBJ Model (armchair.obj) but it's not being loaded. I'm using React three fiber library.
Here's my codesandbox: CodeSandbox
There's no problem with the model because I tried to load it using some other website and it is being loaded.
Anyway, I tried uploading another model (spongebob.obj) but it's not being really visible
However, in the other website, it's visible:
So, here's my codesandbox link
But, if you prefer the code here:
My App.js component:
import React, { Suspense } from "react";
import { Canvas } from "#react-three/fiber";
import { OrbitControls } from "#react-three/drei";
import LoadModel from "./components/LoadModel";
import Loader from "./components/Loader";
const App = () => {
return (
<main className="main-area">
<div id="canvas-container">
<Canvas pixelratio={[1, 2]} camera={{ position: [15, 15, 15], fov: 50, scale: [2,2,2] }}>
<ambientLight intensity={1} />
<Suspense fallback={<Loader />}>
<LoadModel url="./spongebob.obj" />
</Suspense>
<OrbitControls />
</Canvas>
</div>
</main>
);
};
export default App;
My LoadModel.js component:
import React, { useMemo, useState } from "react";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
const LoadModel = ({ url }) => {
const [obj, set] = useState();
// useMemo(() => new OBJLoader().load(url, set), [url])
useMemo(() => new OBJLoader().load(url, set), [url]);
//useMemo(() => new GLTFLoader().load(url, set), [url])
return obj ? <primitive object={obj} dispose={null} /> : null;
};
export default LoadModel;

You can improve the rendering of the SpongeBob model by adding a directional light to your scene. A single ambient light is not sufficient for proper illumination. Try adding the following line to your codesandbox:
<directionalLight />
The chair model has unfortunately some design issues. It has an extreme scale and is highly translated. I suggest you scale the model down and then center it after the loading process. However, it would be better to fix the model in a DCC tool like Blender and model the chair according to real world units.

Related

Issue using eventHandlers with react-leaflet and next.js

I'm trying to use eventHandlers with react-leaflet in a next.js application, but the events are never being fired.
Here is an example of the code I'm using in my Map component:
import { MapContainer, Rectangle } from "react-leaflet";
const Map = () => {
return (
<MapContainer center={[25, 25]} zoom={1} style={{ height: 600, width: 600 }}>
<Rectangle
bounds={[
[0, 0],
[50, 50],
]}
eventHandlers={{
click: () => console.log("clicked.."),
}}
/>
</MapContainer>
);
};
export default Map;
I've tried using this code outside of a next.js application (plane react app) and it worked as expected, but when I use it in a next.js application, the event is never fired.
Here is an example of how I'm rendering the Map component in my index.js file:
import dynamic from "next/dynamic";
const Map = dynamic(() => import("../components/Map"), {
ssr: false,
});
export default function Home() {
return (
<>
<Map />
</>
);
}
I also tried using this code in a codesandbox, but I couldn't get the sandbox to work (it crashed). Also tested it on the live editor on https://react-leaflet.js.org/docs/example-popup-marker/ and replacing the marker with the rectangle component, the event is triggered on click.
Has anyone else experienced this issue and found a solution?
Thanks in advance for any help!

React Three Fiber, Three.js and Drei: lazy import models using components gets re-rendered to infinity

I'm trying to lazy import JSX models in React Three Fiber.
I have a couple of JSX models which incorporate useGLTF() from drei, to load the actual .gltf/.glb model, as nodes and materials, like in the R3F tutorial.
What I'm trying to do now, is to lazy import those JSX model components on demand, through my ModelsLoader component.
The problem is that even when I use memo() or useMemo() from React, the component gets re-rendered constantly...
I don't know what I'm doing wrong, or even if what I'm trying to do is even possible, but hey, there are a lot of smart people here, some maybe you'll be able to help me ;)
example of my JSX model component, which I'm trying to lazy import in ModelsLoader component:
import React, { useRef } from 'react';
import { useGLTF } from '#react-three/drei';
export default function OCF155(props) {
const { nodes } = useGLTF('models/EXAMPLE_MODEL.glb');
const ref = useRef();
return (
<group {...props} ref={ref} dispose={null} type='TopParent'>
<mesh
name='EXAMPLE_MODEL'
castShadow
receiveShadow
geometry={nodes.EXAMPLE_MODEL.geometry}
material={nodes.EXAMPLE_MODEL.material}
rotation={[Math.PI / 2, 0, 0]}
scale={1}
/>
</group>
);
}
And here is my ModelsLoader component as it's stands for now, using .memo():
import React, { memo, lazy } from 'react';
import Selection from './helpers/Selection';
const ModelsLoader = memo(function ModelsLoader({ models }) {
return (
<>
{models &&
models.length > 0 &&
models.map((model, index) => {
const ModelComponent = lazy(() => import(`./models/${model.name}`));
return (
<Selection key={index}>
<ModelComponent
position={model.position}
rotation={model.rotation}
/>
</Selection>
);
})}
</>
);
});
export default ModelsLoader;
I was trying to do it without .memo(), but the result was the same. I was thinking, that if I use .memo(), hence the models list is not changing throughout the render of the page, it will get rendered once and then passed on...

How to move the camera using react-three-fiber and #react-three/drei?

I'm drawing a 3d model with the following configuration.
react-three-fiber
#react-three/drei
The following is a CodeSandBox with a model drawn.
CodeSandBox-A
The drawn model is close to the top of the canvas, and I want to center it.
Could you please tell me how to center the model on the canvas?
This is the first time I've touched Three.js, so I'm sure I may have made some elementary mistakes.
The code is as follows.
I referred to the following document for the props of the Canvas component.
React Three Fiber Documentation
import { useMemo, Suspense } from "react";
import "./style.css";
import { Canvas } from "#react-three/fiber";
const { useGLTF, OrbitControls, Stage } = require("#react-three/drei");
const CanvasContainer = () => {
const { scene } = useGLTF("/scene.glb");
return (
<Canvas camera={{ fov: 70, position: [0, 1000, 1000] }}>
// No matter what I put in this "position" value, I could not see any change in the canvas.
<OrbitControls enablePan={true} enableZoom={true} enableRotate={true} />
<Stage>
<group dispose={null}>
<primitive scale={[1, 1, 1]} object={scene} />
</group>
</Stage>
</Canvas>
);
};
const App = () => {
const isWindow = typeof window === "undefined";
const memoCanvas = useMemo(() => <CanvasContainer />, []);
return (
<>
<h1>Canvas</h1>
<div className="canvas_container">
{!isWindow && <Suspense fallback={<div />}>{memoCanvas}</Suspense>}
</div>
</>
);
};
export default App;
setDefaultCamera removed in v6.x. · Issue #1147 · pmndrs/react-three-fiber
I also tried the code to use useThree as described in the above Issue.
The following is its CodeSandBox.
CodeSandBox-B
As you can see from the CodeSandBox, I was unable to move the camera.
give orbitcontrols the "makeDefault" prop, then it should in the center. otherwise check out dreis Bounds component. see: https://twitter.com/0xca0a/status/1453807293074661385
the memocanvas thing btw makes no sense, don't put jsx into usememo.

Cannot update a component while rendering a different component - Environment / React- Three-Fiber

My App.jsx
import "./App.css";
import { Canvas } from "#react-three/fiber";
import React, { Suspense } from "react";
import { Html, useProgress } from "#react-three/drei";
const Scene = React.lazy(() => import('./components/Scene.jsx'));
function Loader() {
const { progress } = useProgress();
return <Html center>{progress} % loaded</Html>
}
function App() {
return (
<div id="canvas">
<Canvas>
<Suspense fallback={<Loader />}>
<Scene />
</Suspense>
</Canvas>
</div>
);
}
export default App;
My Scene.jsx
function Scene() {
<>
<ambientLight intensity={0.2} />
<directionalLight />
<GizmoHelper
alignment="bottom-right"
margin={[80, 80]}
>
<GizmoViewport
axisColors={["red", "green", "blue"]}
labelColor="black"
/>
</GizmoHelper>
<Text
position={[-3, 0, 0]}
anchorX="center"
anchorY="middle"
fontSize="1.5"
outLineBlur="5"
outlineColor="red"
curveRadius="2.9"
font="db.onlinewebfonts.com/t/02654c5f87934978cd2129477ad40869.ttf">
Hello world
<meshStandardMaterial
color="#ec2d2d"
opacity="1"
roughness="0"
metalness="0.3"
normalMap={normalMap}
/>
</Text>
<OrbitControls />
<Environment preset="city" background="true" />
</>
);
}
My problem:
Every time I start the app the environment doesn't load and I get the following error:
Warning: Cannot update a component (`Loader`) while rendering a different component (`Environment`)
If I change the preset while the live server is running the environment updates and shows instantly.
I have already tried to write a function and render the environment only when the window is loaded but I still got the same error.
I have read somewhere I could use a setTimeout but not sure whether this the right approach.
If anyone could point me in the right direction.
Update:
I did not include the fact that I was using the GizmoHelper component.
Removing the GizmoHelper from the scene solved the problem.
I have added the GizmoHelper in above code in case someone else has the same problem.
Further Update:
I noticed the error is still logged in the console but now it does not affect the initial rendering of the environment anymore.
Might still cause an issue further down the road though.

How to implement Three.js inside Gatsby?

I've installed react-three-fiber and three packages. I'm following this tutorial, but I have doubts of where to put this line:
const rootElement = document.getElementById("root");
Also, I'm starting to receive this error:
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
My index.js:
import React, { Children, useRef, useState } from "react"
import { Link } from "gatsby"
import { Canvas} from 'react-three-fiber'
import Layout from "../components/layout"
const IndexPage = () => {
return(
<Layout>
<div>
<h1>Hi</h1>
</div>
<canvas>
<Children></Children>
</canvas>
</Layout>
)
}
const rootElement = document.getElementById("root");
export default IndexPage
Any ideas?
Make sure you have installed both three and react-three-fiber.
npm install three react-three-fiber
Then in your gatsby page component just import Canvas from react-three-fiber before using it in your JSX.
import React from "react"
import { Canvas} from 'react-three-fiber'
import Layout from "../components/layout"
const IndexPage = () => (
<Layout>
<Canvas />
</Layout>
)
export default IndexPage
Regarding const rootElement = document.getElementById("root"); :
Although Gatsby is built with React, it doesn't require you to select a root element in order to render your app. If this sounds confusing, you should spend a little bit of time to take a read of the Gatsby docs.
If you were to build the example from the react-three-fiber docs in Gatsby, it would look something like this.
import React, { useRef, useState } from "react"
import { Canvas, useFrame } from "react-three-fiber"
const Box = props => {
// This reference will give us direct access to the mesh so we can animate it
const mesh = useRef()
// Set up state for the hovered and active state
const [hovered, setHover] = useState(false)
const [active, setActive] = useState(false)
// Rotate mesh every frame, this is outside of React without overhead
useFrame(() => (mesh.current.rotation.x = mesh.current.rotation.y += 0.01))
return (
<mesh
{...props}
ref={mesh}
scale={active ? [1.5, 1.5, 1.5] : [1, 1, 1]}
onClick={e => setActive(!active)}
onPointerOver={e => setHover(true)}
onPointerOut={e => setHover(false)}
>
<boxBufferGeometry attach="geometry" args={[1, 1, 1]} />
<meshStandardMaterial
attach="material"
color={hovered ? "hotpink" : "orange"}
/>
</mesh>
)
}
const IndexPage = () => (
<Canvas>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
</Canvas>
)
export default IndexPage

Resources