A-frame exit vr mode on function - three.js

I am wondering using A-frame (https://aframe.io) how I can get a user to exit vr mode if they're in vr mode when a function called myFunction() occurs. Just for clarification. When a function called myFunction() occurs, if the user isn't in vr mode, there won't we an effect but if the user is in vr mode, they will be exited from vr mode. How can this be done?

This code worked for me,
import {useThree} from "#react-three/fiber";
const ABC = ({ x, y, x}) => {
const { gl } = useThree();
const exitXR = async (gl) => {
const session = gl.xr.getSession();
if ( session !== null ) {
await session.end();
}
}
return (
<group>
<Html>
<div onClick={()=>exitXR(gl)}> EXIT </div>
</Html>
</group>
)
}

Assuming you have a reference to the renderer, you should be able to do the following:
async function exitXR( renderer ) {
const session = renderer.xr.getSession();
if ( session !== null ) {
await session.end();
// execute optional code after WebXR shutdown
}
}

In A-Frame you can detect if the user is in vr or not with :
sceneEl.is('vr-mode')
https://aframe.io/docs/1.3.0/core/scene.html#states
Then just exit vr with the exitVR() method :
sceneEl.exitVR()
https://aframe.io/docs/1.3.0/core/scene.html#methods
You can get the sceneEl with:
const sceneEl = document.querySelector('a-scene');
In conclusion, just do:
const sceneEl = document.querySelector('a-scene');
if (sceneEl.is('vr-mode')) sceneEl.exitVR();

Related

useEffect executes with stale information

If I have a useEffect hook like this:
const [text, setText] = useState("");
useEffect(() => {
async function run() {
// fetch new text when some prop changes
const newText = await fetchText();
// to exaggerate the effect of waiting,
// let's sleep for two seconds
await new Promise((r) => setTimeout(r, 2000));
setText(newText);
}
run();
}, [some_prop]);
Every time some_prop changes, I fetch new text from an endpoint. Suppose run takes 3 seconds to finish. If the value of some_prop changes more often that, useEffect will still resolve all the async calls to run. Suppose some_prop changes from a -> b -> c -> d -> e in one second. I would like to see the value of text go smoothly from a to e, but it will flash through all the intermediate values (b -> c -> d) as the calls to run finish. How can I throw away those intermediate calls?
Add a let variable (cancel) to the useEffect block, and if the useEffect is called, set cancel to true. If cancel is called, avoid setting the state.
Note: The obvious solution in the example is to cancel the timeout, but the timeout simulates an api call.
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
<script src="https://unpkg.com/#babel/standalone/babel.min.js"></script>
<script type="text/babel">
const { useState, useEffect } = React;
const Demo = () => {
const [num, setN] = useState(0);
const [text, setText] = useState('Text: ');
useEffect(() => {
let cancel = false;
async function run() {
// simulate api call
await new Promise(r => setTimeout(r, 2000));
if(cancel) return;
setText(`Text: ${num * 10}`);
}
run();
return () => {
cancel = true;
};
}, [num]);
return (
<div>
<button onClick={() => setN(n => n + 1)}>{num}</button>
<div>{text}</div>
</div>
);
}
ReactDOM
.createRoot(root)
.render(<Demo />);
</script>
One caveat is that the api is getting called needlessly, and we don't cancel the requests. We can cancel the actual request, by using fetch or any a library that supports cancelling (axios for example).
If you're using fetch, you just need to pass the signel from an abort controller.
fetch(url, { signal })
Read the docs of other libraries to see how they can be cancelled.
This example uses an abort controller to cancel the timeout.
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
<script src="https://unpkg.com/#babel/standalone/babel.min.js"></script>
<script type="text/babel">
const { useState, useEffect } = React;
const Demo = () => {
const [num, setN] = useState(0);
const [text, setText] = useState('Text: ');
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
async function run() {
// simulate api call
await new Promise(r => {
const timeout = setTimeout(r, 2000);
signal.addEventListener('abort', () => {
clearTimeout(timeout);
});
});
setText(`Text: ${num * 10}`);
}
run();
return () => {
controller.abort();
};
}, [num]);
return (
<div>
<button onClick={() => setN(n => n + 1)}>{num}</button>
<div>{text}</div>
</div>
);
}
ReactDOM
.createRoot(root)
.render(<Demo />);
</script>

React Three Fiber / Orbit Controls

Hello
I made a component for orbitControls which doesn't work :
extend({ OrbitControls })
const OrbitA = () => {
const { camera, gl } = useThree()
return (
<OrbitControls
args={[camera, gl.domElement]}
/>
)
}
I have found a workaroud with useEffect :
extend({ OrbitControls })
const Orbit = () => {
const { camera, gl } = useThree()
useEffect(() => {
const controls = new OrbitControls(camera, gl.domElement)
controls.minDistance = 3
controls.maxDistance = 20
return () => {
controls.dispose()
}
}, [camera, gl])
return null
}
But in the last case, i don't know how to pass the attach parameter like I would have done it in the first case :
return (
<OrbitControls
attach='orbitControls'
args={[camera, gl.domElement]}
/>
)
}
Any help ?

Three.js doesn't play animation in React

I load GLTF model downloaded from Sketchfab in React app. Model loads perfectly well, but animation doesn`t play at all. I tried different approaches. Any ideas?
function Model({ url }) {
const model = useRef()
const { scene, animations } = useLoader(GLTFLoader, url)
const [mixer] = useState(() => new THREE.AnimationMixer())
useEffect(() => void mixer.clipAction(animations[0], group.current).play(), [])
return(
<primitive
ref={model}
object={scene}
/>
)
}
Solution
I had to use node instead of scene and select "RootNode" (console.log node and choose what seemed to me the main node, the model contained a dozen of nodes)
Update mixer by frames with useFrame
Apply animation to the model (a bit updated)
Working code:
function Model({ url }) {
const group = useRef()
const { nodes, scene, materials, animations } = useLoader(GLTFLoader, url)
const actions = useRef()
const [mixer] = useState(() => new THREE.AnimationMixer())
useFrame((state, delta) => mixer.update(delta))
useEffect(() => {
actions.current = { idle: mixer.clipAction(animations[0], group.current) }
actions.current.idle.play()
return () => animations.forEach((clip) => mixer.uncacheClip(clip))
}, [])
return(
<group ref={group} dispose={null}>
<primitive
ref={group}
name="Object_0"
object={nodes["RootNode"]}
/>
</group>
)
}
Now it works
You need to call mixer.update(delta) in useFrame inside your component:
import { useFrame } from 'react-three-fiber'
function Model({url}) {
.
.
.
useFrame((scene, delta) => {
mixer?.update(delta)
})
.
.
.
Put your glb file inside public filer.
import { Suspense, useEffect, useRef } from "react";
import { useAnimations, useGLTF } from "#react-three/drei";
export const Character = (props: any) => {
const ref = useRef() as any;
const glf = useGLTF("assets/Soldier.glb");
const { actions } = useAnimations(glf.animations, ref);
useEffect(() => {
actions.Run?.play();
});
return (
<Suspense fallback={null}>
<primitive
ref={ref}
object={glf.scene}
/>
</Suspense>
);
};

React Hooks useState promptly Update

How can I promptly update the number state and watch console.log(number) updated value?
const [number,setNumber] = useState(0);
const minus = () => {
setNumber(number-1);
console.log(number);
}
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
)
What you are trying to do is a side-effect: print something onto the console.
This is what useEffect hook is for - it lets you perform side effects.
So here is a possible implementation:
function App() {
const [number, setNumber] = useState(0);
const minus = () => {
setNumber(number - 1);
};
useEffect(() => {
console.log(number);
}, [number]);
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
);
}
Of course, it may be an overkill solution if you are just using console.log for debugging purpose. If that's the case, #zynkn and #deepak-k's answers work just fine.
Try this
setNumber((number)=> {number-1 ; console.log(number)});
const [number,setNumber] = useState(0);
const minus = () => {
// setNumber(number-1);
// It is also work well but it is working with async.
// So you can't see the below console.log().
// console.log(number);
setNumber((prevNumber) => {
newNumber = prevNumber - 1;
console.log(newNumber);
return newNumber;
});
}
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
)

Disable user typing text box c#

Is it possible to disable the user typing input text area in the V4 bot framework in any channel ? I have this as part of customer requirement Can someone please help me
The box you refer to is called the send box. If you are using BotFramework-Web Chat, you can disable it by passing the value via styleOptions like so:
<script>
(async function () {
const styleOptions = {
hideSendBox = true
}
[...]
window.ReactDOM.render(
<ReactWebChat
directLine={directLine},
styleOptions={styleOptions}
/>,
document.getElementById( 'webchat' )
);
})
</script>
If you are using the iFrame embedded version of Web Chat, it is not configurable.
Hope of help!
Edit
If you are wanting the send box to be responsive according to the type of activity received from the bot, then you will want to use a combination of the activityMiddleware() function as well as an event emitter/listener. In the following example, I am hiding/showing the send box when suggestedActions is an activity property.
Please be aware that the data values should be "none" and "flex". In particular, the latter value when it is not suggestedActions in order to maintain the current code.
<script>
(async function () {
[...]
const activityMiddleware = () => next => card => {
const { activity: { suggestedActions } } = card;
const toggleSendBoxEvent = new Event('ToggleSendBoxEvent')
if (suggestedActions) {
toggleSendBoxEvent.data = "none";
window.dispatchEvent(toggleSendBoxEvent);
} else {
toggleSendBoxEvent.data = "flex";
window.dispatchEvent(toggleSendBoxEvent);
}
return next(card);
)
[...]
window.ReactDOM.render(
<ReactWebChat
directLine={ window.WebChat.createDirectLine({ token }) }
activityMiddleware={ activityMiddleware }
/>,
document.getElementById( 'webchat' )
);
window.addEventListener('ToggleSendBoxEvent', ( { data } ) => {
const sendBoxes = document.getElementsByClassName("main");
let send_Box;
for (let sendBox of sendBoxes) {
send_Box = sendBox;
}
send_Box.setAttribute('style', `display:${ data }`)
})
});
</script>
Hope of help!

Resources