Sphere not respecting gravity outside plane geometry - react-three-fiber

I have this simple environment with a sphere and a plane. Both of the objects are wrapped within a Physics component and the physics work, if I remove the plane the sphere will fall. But when I run the sphere outside the plane, the sphere does not fall. I don't know if I have misunderstood something trivial, maybe the plane is wider than what it looks like for example?
I don't think there is a way to set the width/depth of the mesh, so I assume it is the width/depth of the planeBufferGeometry that counts?
main.js:
import { Canvas } from '#react-three/fiber'
import { Sky, PointerLockControls } from '#react-three/drei'
import { Physics } from '#react-three/cannon'
import { Ground, Sphere } from '../components/'
export default function Game(props) {
return (
<>
<Canvas shadows colorManagement camera={{ fov: 45 }}>
<Sky sunPosition={[100, 10, 100]} />
<ambientLight intensity={0.3} />
<pointLight castShadow intensity={2.8} position={[100, 100, 100]} />
<Physics gravity={[0, -106, 0]}>
<Ground />
<Sphere />
</Physics>
<PointerLockControls />
</Canvas>
</>
)
}
Ground.js:
import { usePlane } from '#react-three/cannon'
import { useNormalTexture } from '#react-three/drei'
export default function Ground(props) {
const [ref] = usePlane(() => ({ rotation: [-Math.PI / 2, 0, 0], ...props }))
const [normalMap] = useNormalTexture(32, {
offset: [0, 0],
repeat: [100, 100],
anisotropy: 8
})
return (
<mesh ref={ref} receiveShadow>
<planeBufferGeometry attach="geometry" args={[50, 50]} />
<meshStandardMaterial
attach="material"
normalMap={normalMap}
color="#dadb84"
/>
</mesh>
)
}

Related

react-three-fiber: click with orthographic camera problem

clicks work well with the perspective camera, but don't work with the orthographic camera. If the orthographic camera is enabled, then dead zones appear in which the click does not work. How to make click work correctly with orthographic camera?
https://codesandbox.io/s/damp-water-2oqlvn?file=/src/index.js
function Thing() {
return (
<mesh
onClick={(e) => {
console.log('click')
}}>
<boxGeometry args={[10, 10, 10]} />
<meshNormalMaterial />
</mesh>
)
}
const App = () => {
return (
<Canvas
dpr={window.devicePixelRatio}
gl={{
antialias: true
}}
camera={{
far: Number.MAX_SAFE_INTEGER,
position: [0, 1, 0],
zoom: 10
}}
orthographic>
<Grid infiniteGrid sectionColor="#fff" fadeDistance={100} sectionThickness={0.5} sectionSize={10} />
<Thing />
<CameraControls />
</Canvas>
)
}

How to implement multiple cloud points and interact with them separately?

I display 2 cloud points from a PLY file. I need to trigger events on each cloud point. For example: Move or rotate only 1 cloud points.
My problem:
I think that the event is applied on each points of the 2 cloud points instead of the global model on which I triggered the event. So it takes a very long time to trigger a simple event (like a console.log for example).
Are cloud points correctly implemented?
How to interact with only 1 point cloud?
Code:
Here is the stackblitz to try.
And here is my code:
// 'Stage' and 'OrbitControls' are from #react-three/drei
function App() {
return (
<Canvas camera={{ position: [0, 0, 15] }} orthographic>
<Suspense fallback={null}>
<Stage fallback={null}>
<PLYVisualizationScreen color="green" position={[0, 0, 0]} />
<PLYVisualizationScreen color="blue" position={[-0.2, 0, 0]} />
<OrbitControls makeDefault enableDamping={false} />
</Stage>
<ambientLight intensity={1} />
</Suspense>
</Canvas>
);
}
function PLYVisualizationScreen({ color, position }) {
const geometry = useLoader(PLYLoader, 'bunny.ply');
const ref = useRef();
return (
<points
ref={ref}
position={position}
castShadow
receiveShadow
geometry={geometry}
// onClick={(e) => console.log("click on")}
>
<pointsMaterial
sizeAttenuation
attach="material"
color={color}
depthWrite={false}
size={0.001}
/>
</points>
);
}

React Three Fiber | Lines are not casting shadows

I recently started digging into ThreeJS, followed some tutorials and now I have a problem with my line / bufferGeometry not casting any shadows. Shadows are casted, as long as I use boxBufferGeometry inside a mesh. But if I use lines, nothing happens
import React from 'react'
import { Canvas } from '#react-three/fiber'
import Curve from './components/Curve';
import Controls from './components/Controls';
import Plane from './components/Plane';
import * as THREE from 'three';
const App = () => {
return (
<div className="App">
<Canvas shadows colorManagement camera={{position: [5,5,5]}} onCreated={({gl}) => {
gl.shadowMap.enabled = true;
gl.shadowMap.type = THREE.PCFShadowMap;
gl.setPixelRatio(window.devicePixelRatio || 2);
}}>
<spotLight
position={[0,70,0]}
color="white"
intensity={1.5}
castShadow
shadowMapHeight={1024}
shadowMapWidth={1024}
shadowCameraNear={200}
shadowCameraFar={2000}
shadowBias={ - 0.00022}
angle={0.01}
/>
<ambientLight color="#f0f0f0" />
<Controls />
<Curve />
<Plane />
<gridHelper args={[30, 100, 'white', 'white']} position={[0,-3.99,0]} receiveShadow />
</Canvas>
</div>
);
}
export default App;
import React, { useLayoutEffect, useRef } from 'react';
import * as THREE from 'three';
const Curve = () => {
const curve = new THREE.CubicBezierCurve3
(
new THREE.Vector3( -10, 0, 0 ),
new THREE.Vector3( -5, 15, 0 ),
new THREE.Vector3( 20, 15, 0 ),
new THREE.Vector3( 10, 0, 0 )
);
const points = curve.getPoints( 50 );
const ref = useRef()
useLayoutEffect(() => {void ref.current.setFromPoints(points); }, [points])
return (
<line>
<bufferGeometry ref={ref} />
<lineBasicMaterial color={0xd2452b} />
</line>
);
};
export default Curve;
import React from 'react';
const Plane = () =>
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0,-4,0]} receiveShadow>
<planeGeometry
attach="geometry"
args={[1024,1024]}
/>
<shadowMaterial attach="material" color="#000000" opacity="1" />
</mesh>
export default Plane;
Rendering this component (Box) instead of curve, would cast a shadow.
import React, { useState } from 'react';
import { useSpring, a } from '#react-spring/three';
const Box = () => {
const [hovered, setHovered] = useState(false);
const [active, setActive] = useState(false);
const props = useSpring({
scale: active ? 2 : 1,
color: hovered ? 'red' : 'gray',
});
return (
<a.mesh
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}
onClick={() => setActive(!active)}
scale={props.scale}
castShadow
>
<boxBufferGeometry
attach="geometry"
args={[1,1,1]}
/>
<a.meshPhysicalMaterial attach="material" color={props.color} />
</a.mesh>
);
};
export default Box;
The only difference (I'm still a newbie), is that I am using lines instead of mesh. Mesh accepts attributes like "receiveShadow and castShadow", lines doesn't.
How do I manage to let my lines cast shadows, too?
Thanks a lot in advance

RingBufferGeometry thetaLength animation with GSAP and react-three-fiber

I would like to animate my ringBufferGeometry like in example below, but I would like to use GSAP to animate this ringBufferGeometry for certain amount of time and I would like then to stop it when the thetaLength will equal to some value. Something like in example below but without the use of Math.sin().
three js RingBufferGeometry, update thetaLength
This is what I want to achieve(link below), but I want stop to animate when it reaches the full triangle form.
https://jsfiddle.net/02oyunbj/1/
My code
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
function Container(props) {
return <div style={{ position: 'absolute', inset: 0 }} {...props} />
}
ReactDOM.render(
<Container>
<App />
</Container>,
document.getElementById('root')
)
import { useRef, useEffect } from 'react'
import { Canvas } from '#react-three/fiber'
import { OrbitControls } from '#react-three/drei'
import * as THREE from 'three'
import gsap from 'gsap'
export default function App() {
return (
<Canvas camera={{ position: [0, 0, 15] }}>
<color attach="background" args={['black']} />
<OrbitControls />
<spotLight position={[15, 15, 15]} angle={0.3} color={'white'} />
<TriangleRing />
</Canvas>
)
}
const TriangleRing = () => {
const triangleRingRef = useRef()
const parametersObj = {
innerRadius: 4,
outerRadius: 8,
thetaSegments: 1,
phiSegments: 30,
thetaStart: 1.55,
thetaLength: 0,
}
const parameters = [
parametersObj.innerRadius,
parametersObj.outerRadius,
parametersObj.thetaSegments,
parametersObj.phiSegments,
parametersObj.thetaStart,
parametersObj.thetaLength,
]
useEffect(() => {
console.log(triangleRingRef.current)
const triangleGeometry = triangleRingRef.current.geometry
gsap.set(triangleGeometry, {
parameters: { ...parametersObj, thetaLength: 0 },
})
const tl = gsap.timeline({ defaults: { ease: 'power3.inOut' } })
tl.to(triangleGeometry, {
parameters: { ...parametersObj, thetaLength: 6.3 },
duration: 4,
})
})
return (
<mesh ref={triangleRingRef}>
<ringBufferGeometry args={parameters} name="TriangleRing" />
<meshToonMaterial side={THREE.DoubleSide} color={0xffffff} />
</mesh>
)
}
Link to sandbox:
https://codesandbox.io/s/affectionate-lichterman-edpfr?file=/src/App.jsx:0-1517
By default GSAP works in terms of seconds, not velocity or anything like that. So you'll need to calculate how long the tween(s) should run (by using the difference in value and the rate at which you want it to move) to get it to result in the end value that you need.
Without a minimal, complete, and verifiable example it's hard for us to help more at this point.

Objects don't overlap when rotating

I'm trying to reproduce this animation (see below) with react-three-fiber. I'm still very new to this package and to three-js.
http://makesportmakebook.com/livres/.
I've been able to do create book shapes with meshLambertMaterial, as such:
function Book(props) {
const mesh = useRef();
useFrame(() => {
mesh.current.rotation.x = mesh.current.rotation.y += 0.01
})
const bookCover = useLoader(TextureLoader, bookCoverImg)
const bookSpine = useLoader(TextureLoader, bookSpineImg)
const bookBack = useLoader(TextureLoader, bookBackImg)
const bookPages = useLoader(TextureLoader, bookPagesImg)
const bookPagesTexture = useLoader(TextureLoader, bookPagesTextureImg)
const bookPagesTopBottomTexture = useLoader(TextureLoader, bookPagesTopBottomTextureImg)
return (
<mesh
position={props.position}
ref={mesh}>
<boxBufferGeometry attach="geometry" args={
[
7, 10, 1.2, 4, 4, 1
]
} />
<meshLambertMaterial color={"0xffffff"} map={bookCover} />
<meshLambertMaterial map={bookSpine} />
<meshLambertMaterial map={bookBack} />
<meshLambertMaterial map={bookPages} />
<meshLambertMaterial map={texture_5} />
<meshLambertMaterial map={texture_6} />
</mesh>
)
};
Here's a code sandbox of my code so far: https://codesandbox.io/s/cocky-fast-61ndj
My question is: how can I avoid the overlap of these you can see here (below) but still keep the same parallel position we can see in the first example?
There are two ways that you could handle this. The first is to move the camera instead of the books. The second is that instead of moving the books separately, move them as a group. I suspect that this second method is what you want to do.
This was quick and dirty, but it works.
Create a function to house the two books. Put the two books in a group and rotate the group.
import React, { useRef, Suspense } from "react";
import { Canvas, useFrame, extend, useLoader } from "react-three-fiber";
import { OrbitControls, StandardEffects, draco } from "drei";
import { TextureLoader } from "three/src/loaders/TextureLoader.js";
extend({ OrbitControls });
function Bookshelf(props) {
const mesh = useRef();
useFrame(() => {
mesh.current.rotation.x = mesh.current.rotation.y += 0.01;
});
return (
<group ref={mesh} position={[0, 0, 0]}>
<Suspense fallback={null}><Book position={[3, 0, 3]} /></Suspense>
<Suspense fallback={null}><Book position={[-3, 0, 0]} /></Suspense>
</group>
)
}
function Book(props) {
const bookCover = useLoader(
TextureLoader,
"https://res.cloudinary.com/www-c-t-l-k-com/image/upload/v1607732427/HEAD_PUBLISHING/book-cover.jpg"
);
const bookSpine = useLoader(
TextureLoader,
"https://res.cloudinary.com/www-c-t-l-k-com/image/upload/v1607732420/HEAD_PUBLISHING/book-back.jpg"
);
const bookBack = useLoader(
TextureLoader,
"https://res.cloudinary.com/www-c-t-l-k-com/image/upload/v1607732421/HEAD_PUBLISHING/book-side.jpg"
);
const bookPages = useLoader(
TextureLoader,
"https://res.cloudinary.com/www-c-t-l-k-com/image/upload/v1607732421/HEAD_PUBLISHING/book-side.jpg"
);
const texture_5 = useLoader(
TextureLoader,
"https://res.cloudinary.com/www-c-t-l-k-com/image/upload/v1607732427/HEAD_PUBLISHING/book-cover.jpg"
);
const texture_6 = useLoader(
TextureLoader,
"https://res.cloudinary.com/www-c-t-l-k-com/image/upload/v1607732421/HEAD_PUBLISHING/book-spine.jpg"
);
return (
<mesh position={props.position} >
<boxBufferGeometry attach="geometry" args={[7, 10, 1.2, 4, 4, 1]} />
<meshLambertMaterial color={"0xffffff"} map={bookCover} />
<meshLambertMaterial map={bookSpine} />
<meshLambertMaterial map={bookBack} />
<meshLambertMaterial map={bookPages} />
<meshLambertMaterial map={texture_5} />
<meshLambertMaterial map={texture_6} />
</mesh>
);
}
export default function App() {
let styling = {
width: "100vw",
height: "100vh",
position: "relative"
};
return (
<div style={{ position: "relative" }}>
<Canvas camera={{ position: [0, 0, 20] }} style={styling}>
<ambientLight intensity={0.3} />
<directionalLight intensity={1} />
<Suspense fallback={null}>
<Bookshelf>
</Bookshelf>
</Suspense>
<OrbitControls enableZoom={false} />
</Canvas>
</div>
);
}

Resources