Threejs and WebGL not rendering 3d objects in firefox - reactjs

Hello everyone I am creating my own website using reactjs and I want to include 3d object in my website.
To do that I am using three js with a WebGL renderer, it usually works fine but SOMETIMES my 3d object does not render and I get a black frame instead (see the pictures). Now the issue is that this bug happens sometimes and I can't understand what produces it. The bug is only happening on firefox, chrome works fine.
3d object bug
3d object working
I know how to solve it for my computer (I have to enable graphic performances for firefox in config panel), yet it was working perfectly fine sometimes without graphic performances enabled. My fear is that if I deploy my website this bug occurs for other user.
(Edit after some research this is EXACTLY the issue https://bugzilla.mozilla.org/show_bug.cgi?id=1712486 but i don't get how to solve the issue)
I am pretty sure that this is due to the WebGL renderer which seems to sometimes have trouble to load textures with my graphic card... not sure why.
Can anyone help me to find a fix (for every user not only for my computer) or is there a way to check if this rendering bug happens in order to display an image instead ?
Here is the code to generate my object :
import React from 'react';
import './object3d.css';
import { useState, useEffect, useRef } from "react";
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
var sat;
function loadGLTFModel(scene, glbPath) {
return new Promise((resolve, reject) => {
const loader = new RGBELoader();
//loader.load(
loader.load( 'royal_esplanade_1k.hdr', ( texture ) => {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = texture;
const loader2 = new GLTFLoader();
loader2.load( glbPath, function ( gltf ) {
sat = gltf.scene
scene.add( sat );
resolve(sat);
} );
},
undefined,
function (error) {
console.log(error);
reject(error);
} );
});
}
const Object = ({path_file}) => {
const refContainer = useRef();
const [loading, setLoading] = useState(true);
const [renderer, setRenderer] = useState();
useEffect(() => {
const { current: container } = refContainer;
if (container && !renderer) {
const scW = container.clientWidth;
const scH = container.clientHeight;
//console.log(scW);
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
premultipliedAlpha : false
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(scW, scH);
renderer.outputEncoding = THREE.sRGBEncoding;
container.appendChild(renderer.domElement);
setRenderer(renderer);
const scene = new THREE.Scene();
const scale = 25;
const camera = new THREE.OrthographicCamera(
-scale,
scale,
scale,
-scale,
0.01,
50000
);
camera.position.y = 25;
camera.position.x = 2;
camera.position.z = 0;
camera.rotation.y = 45* Math.PI /180;
camera.rotation.z = 45* Math.PI /180;
const ambientLight = new THREE.AmbientLight(0xcccccc, 1);
scene.add(ambientLight);
loadGLTFModel(scene, path_file, {
}).then((sat) => {
animate(sat);
setLoading(false);
});
let req = null;
const animate = () => {
req = requestAnimationFrame(animate);
sat.rotation.y+=0.01;
renderer.render(scene, camera);
};
return () => {
cancelAnimationFrame(req);
renderer.dispose();
};
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className='container_3d'
ref={refContainer}
>
{loading && (
<span style={{ left: "50%", top: "50%" }}>
Loading...
</span>
)}
</div>
);
};
const Object3dsat = ({path_file}) => {
return (
<Object path_file={path_file}/>
)
}
export default Object3dsat
I notted that when the bug occurs I got multiple lines in the Fail Logs :
"RenderDXGITextureHost Failed to open shared texture, hr=0x80070057"
More Over my config :
GPU 1 : AMD radeon RX Vega 10
GPU 2 : Nvidia GeForce RTX 2060
CPU : AMD Ryzen 7 3750H with Radeon Vega Mobile Gfx
and I am using Firefox Version: 105.0.1
(I also notted that the GPU used for Web rendering is the AMD Radeon GPU but it was working even with this GPU)
Hope you guys can help me
Thank you for your help !

Related

Is there any memory problem that "Too many active WebGL contents." keeps coming out in my web console

Now I'm using RTSP stream with jsmpeg, ffmpeg, node-rtsp-stream in React.js
I set multiple streaming in react component about 1 to maximum 25
and when I change route to many , It apeard my console
I'm a little afraid of if it caused by memory leak problem
I cannot figure out it is okay or critical problem
Could you tell me how to solve this problem or is it fine
// out of component
const streams: Streams = {};
...
const { preset } = props;
const elementsRef = useRef<any>(new Array(preset?.partition).fill(0).map((v) => createRef()));
// Mount
useEffect(() => {
const { JSMpeg } = window;
const sortedStream = _.sortBy(preset?.stream, ["position"]);
console.log(sortedStream);
sortedStream.forEach((v, i: number) => {
const player = new JSMpeg.Player(v.camera_monitoring.ws, {
canvas: elementsRef?.current[v.position]?.current, // Canvas should be a canvas DOM element
});
console.dir(elementsRef?.current[v.position]?.current);
streams[v.id] = player;
});
}, []);
// UnMount
useEffect(() => {
return () => {
Object.keys(streams).forEach((v) => {
console.log("unmount key:", v);
if (streams[v] !== null) {
streams[v].destroy();
streams[v] = null;
}
});
};
}, []);
...
https://github.com/phoboslab/jsmpeg
above library `jsmpeg.min.js` is set by global ( in public directory and set in my html )
actually my code are so many antipattern in contrast react style, To make an excuse, It is my limits of one's ability

Incorporate and display JSON file of global lat, lon, and temp data with D3.js + React Globe?

My current job needs me to develop a 3d globe application using D3.js, and React.js, that incorporates a huge JSON file of global latitudes, longitudes, and temperature data. I have built a couple versions of the globe, and can get them functioning (they spin, and have JSON data of countries and landmass loading in perfectly), but incorporating the JSON file of the other data is proving much more difficult than I expected. Ideally, if you click anywhere on the globe, the relevant info (lat, lon, max temp, min temp, and median temp) will print into the browser, but I just can't figure this out. Let me just say I'm a very junior dev, and this is probably a simple solution that I'm just not seeing :(
I used one JSON file to load countries data and landmass data, and mapped through it. This worked.
I'm using a second JSON file that has lat, lon, and temp data. Essentially, if I map through this file, I can print it to the console, where half the time everything freezes because the JSON file is so massive. But affixing it to the actual globe is proving to be really difficult. I'm not an expert in D3.js, but my job needed me to learn it quick, I feel like I've made a lot of progress but this one area is throwing me.
Access the countries + landmass correctly-
`
import React, {useState, useEffect} from "react";
import { json } from 'd3';
import { feature, mesh } from 'topojson-client';
const jsonUrl =
"https://unpkg.com/world-atlas#2.0.2/countries-50m.json";
export const useData = () => {
const [data, setData] = useState(null);
useEffect(() => {
json(jsonUrl).then(topojsonData => {
const {countries, land} = topojsonData.objects
setData (
{
land: feature(topojsonData, land),
interiors: mesh(topojsonData, countries, (a, b) => a !== b),
}
)
});
}, [])
return data;
};
`
Able to access the lat, lon, and temp-
`
var data3 = require("./data/out.json")
export const ClimateData = () => {
let climateData = data3;
return climateData;
};
`
Main build file (I've commented out what I've tried to use so far, these have printed the data to the console though not to the globe itself)-
`
import { geoOrthographic, geoPath } from 'd3';
import React, { useState, useCallback } from 'react';
import $ from 'jquery';
import { ClimateData } from './ClimateData';
import styled from 'styled-components';
const width = 960;
const height = 500;
const initialMousePosition = {x: width/2, y:height/2};
const projection = geoOrthographic();
const path = geoPath(projection);
const climateData = ClimateData();
export const Marks = ({data: {land, interiors}}) => {
const [MousePosition, SetMousePosition] = useState(initialMousePosition);
const [mouseDown, SetMouseDown] = useState(false);
const handleMouseDown = useCallback((event) => {
SetMouseDown(true);
}, [])
const handleMouseUp = useCallback((event) => {
$('.marks').css('cursor', '')
SetMouseDown(false);
}, [])
const handleMouseMove = useCallback((event) => {
const {clientX, clientY} = event;
if(mouseDown) {
SetMousePosition({x:clientX, y:clientY});
$('.marks').css('cursor', 'pointer')
}
}, [SetMousePosition, mouseDown]);
return(
<svg width="1200" height="1200">
<g className='marks'
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
>
{projection.rotate([MousePosition.x + 30 / 60, -MousePosition.y, 0])}
{/* access map and globe data */}
<path className='sphere' d={path({type: 'Sphere' })}/>
{land.features.map(feature => (
<path className='feature' d={path(feature)}/>
))}
<path className='interiors' d={path(interiors)}/>
{/* access climate data below */}
{/* {climateData.map(climate => (
<path className='climate' key={climate.lat} d={path(climate.lat)}/>
, console.log(climate.lat, 'LATITUDES TEST')
))}
</g>
</svg>
);
}
export default Marks;
`
A mini version of the JSON data I'm trying to incorporate-
[{
"lon":0.0,
"lat":90.0,
"tmax":262.0498352051,
"tmin":256.5426025391,
"tmed":259.2962036133
},
{
"lon":0.0,
"lat":89.5,
"tmax":261.589050293,
"tmin":256.0026245117,
"tmed":258.7958374023
}
]

How do I access the 'currentImageIdIndex' when using the stack scroll tool in Cornerstone.js in a React functional component?

I'm currently successfully displaying a stack of images in a React component but am unsure where to place an event listener in order to access the currentImageIdIndex when scrolling.
import React, { useEffect, useRef, useCallback } from "react";
import cornerstone from "cornerstone-core";
import cornerstoneMath from "cornerstone-math";
import cornerstoneTools from "cornerstone-tools";
import cornerstoneFileImageLoader from "cornerstone-file-image-loader";
import Hammer from "hammerjs";
function StackImageViewport(props) {
const viewerRef = useRef(null);
const base64StringToArrayBuffer = useCallback((base64) => {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}, []);
const initializeCornerstone = () => {
// Initialise cornerstone and link to DOM element
cornerstoneTools.external.cornerstone = cornerstone;
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
cornerstoneFileImageLoader.external.cornerstone = cornerstone;
cornerstoneTools.external.Hammer = Hammer;
cornerstoneTools.init();
cornerstone.enable(viewerRef.current);
};
const setCornerstoneTools = () => {
// define Cornerstone Tools
const StackScrollTool = cornerstoneTools.StackScrollTool;
const StackScrollMouseWheelTool =
cornerstoneTools.StackScrollMouseWheelTool;
const WindowingTool = cornerstoneTools.WwwcTool;
// Add tools
cornerstoneTools.addTool(StackScrollTool);
cornerstoneTools.addTool(StackScrollMouseWheelTool);
cornerstoneTools.addTool(WindowingTool);
// set tools to Active state
cornerstoneTools.setToolActive("StackScroll", { mouseButtonMask: 1 });
cornerstoneTools.setToolActive("StackScrollMouseWheel", {});
cornerstoneTools.setToolActive("Wwwc", { mouseButtonMask: 2 });
};
const displayStack = (stackMediaArray) => {
let mediaArray = [];
// 'stackMediaArray' is an array of images, each containing a buffer of the image
Promise.all(
stackMediaArray.map((mediaObject) => {
return new Promise((resolve, reject) => {
let imageBuffer = base64StringToArrayBuffer(mediaObject.buffer);
const imageId =
cornerstoneFileImageLoader.fileManager.addBuffer(imageBuffer);
mediaArray.push(imageId);
resolve(mediaObject);
}).catch(console.error);
})
);
//define the stack
const stack = {
currentImageIdIndex: 0,
imageIds: mediaArray,
};
// load images and set the stack
cornerstone.loadAndCacheImage(mediaArray[0]).then((image) => {
cornerstone.displayImage(viewerRef.current, image);
cornerstoneTools.addStackStateManager(viewerRef.current, ["stack"]);
cornerstoneTools.addToolState(viewerRef.current, "stack", stack);
});
setCornerstoneTools();
};
useEffect(() => {
if (!viewerRef.current) {
return;
}
initializeCornerstone();
displayStack(props.stackMediaArray);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [viewerRef]);
return (
<>
<div ref={viewerRef} id="viewer" className="flex h-1/2-screen"></div>
</>
);
}
export default StackImageViewport;
This attempts to answer the question:
https://github.com/cornerstonejs/cornerstoneTools/issues/1121
however, I don't want to access DOM elements to add the event listener to.
It's also clear that events are accessible in Cornertsone.js:
https://github.com/cornerstonejs/cornerstoneTools/blob/master/src/events.js
... but I'm still not sure where to place the event listener ?
Any help would be much appreciated.
Thanks.
Listening to events in ReactJS might be confusing for someone new, even more, when using a new tool such as CornerstoneJS. However, you can create an event listener by using the window.addEventListener method, just like you would in a Vanilla JavaScript. Note that this might change from browser to mobile environments. Moreover, your cornerstoneFileImageLoader can also be a challenge.
For this purpose, you can follow the structure:
window.addEventListener('keydown', (event) => {
...
});
But now, we need to understand "where" to place it. Imagine that all of your pages are just components, as ReactJS is a component-based system. Meaning that the event listener need to happen inside the component.
For instance, you can do like that:
import React from 'react';
const App = (props) => {
window.addEventListener('keydown', (event) => {
...
});
return (
<div className='container'>
<h1>Welcome to the Keydown Listening Component</h1>
</div>
);
};

React onTouch event not functioning properly

I am trying to make a react carousel component with swipe options when accessed on touch screen device. So far I have been able to find only few tutorials about this type of React code. I tried some, but for some reason my images are moving only for small amount of space, no matter how big swipe is made on my touch screen.
Here you can see my lines of code:
import React, {useState, useEffect} from 'react'
import IMGComp from './imgComp'
import './slider.scss'
import i1 from './images/image1.jpg'
import i2 from './images/image2.jpg'
import i3 from './images/image3.jpg'
import i4 from './images/image4.jpg'
import i5 from './images/image5.jpg'
export default function Slider () {
let sliderArr = [<IMGComp src={i1}/>,<IMGComp src={i2}/>,<IMGComp src={i3}/>,<IMGComp src={i4}/>,<IMGComp src={i5}/>]
const [x, setX] = useState(0)
let touchPoint
/* useEffect(() => {
(setX(x+change) + console.log(`this is new ${x}`));
}, [touchmove]);*/
const goLeft = () => {
x===0? setX(-100*(sliderArr.length -1)) : setX(x+100)
}
const goRight = () => {
(x=== -100*(sliderArr.length-1)) ? setX(0) : setX(x-100)
}
const touchstart = (event) =>{
touchPoint = event.touches[0].clientX
}
const touchmove = (event) =>{
let touch = event.touches[0]
let change = Math.floor((touch.clientX - touchPoint)/10)
if (change < 0 ) {
(setX((change)*10) + console.log(`main point is ${change} moving to right`))
} else {
(setX((change)*10) + console.log(`main point is ${change} moving to left`))
}
//event.preventDefault()
}
const touchend = (event) =>{
let change = event.changedTouches[0].clientX - touchPoint
let screenw = ((screen.width / 4)/10)
console.log(screenw)
if (change> screenw) {
console.log(`next screen ${screenw}`)
(x=== -100*(sliderArr.length-1)) ? setX(0) : setX(x-100)
} else {setX(x-(change*10))}
}
return (
<div className="slider">
{
sliderArr.map((item, index)=>{
return(
<div key={index} className="slide" style={{transform: `translateX(${x}%)`}}
onTouchStart={touchstart}
onTouchMove={touchmove}
onTouchEnd={touchend}
>
{item}
</div>
)
})
}
<button id="goLeft" onClick={goLeft}>left</button>
<button id="goRight" onClick={goRight}>right</button>
</div>
)
}
Or you can access the whole repository on github: https://github.com/jilvins/carousel-with-swipes
Can someone tell me how to correct this Carousel item in order to make it usable for touch screens? Good info/tutorial sources about touch events and React based mobile web development would be greatly appreciated too.

How to import TrackballControls from three.js module using react?

I am using react js boiler plate (create-react-app) and imported three.js. I am trying to use TrackballControls for a particular project but it isn't working. It throws an error like "Attempted import error: 'TrackballControls' is not exported from 'three' (imported as 'THREE')" . Now I understand that it is in the examples folder and it's not an official export if I correctly understood from the forum. Some one please help me with this, how do I import TrackballControls in a react component? Help will be highly appreciated!
import React, { useState, useEffect } from 'react'
import * as THREE from 'three'
const OrbitControls = require('three-orbit-controls')(THREE)
import "../src/assets/sass/home.scss"
const X = () => {
const [y, setY] = useState(0)
const [ masses, setMasses ] = useState([])
let parent, renderer, scene, camera, controls
useEffect(() => {
// renderer
renderer = new THREE.WebGLRenderer()
renderer.setSize( window.innerWidth, window.innerHeight )
document.body.appendChild( renderer.domElement )
// scene
scene = new THREE.Scene()
// camera
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 100 )
camera.position.set( 20, 20, 20 )
// controls
controls = new THREE.TrackballControls( camera, sphere )
controls.minDistance = 5
controls.maxDistance = 250
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.dampingFactor = 0.05;
// axes
// scene.add(new THREE.AxisHelper( 20 ))
// geometry
let geometry = new THREE.SphereGeometry( 2, 8, 6, 0, 6.3, 0, 3.1)
// material
let material = new THREE.MeshBasicMaterial({
wireframe: true,
wireframeLinewidth: 1
})
let sphere = new THREE.Mesh( geometry, material )
// parent
parent = new THREE.Object3D()
scene.add( parent )
scene.add( sphere )
function animate() {
requestAnimationFrame( animate )
parent.rotation.z += 0.01
controls.update()
renderer.render( scene, camera )
}
animate()
}
,[])
return <div></div>
}
export default X
All JavaScript files from the examples directory are now available as ES6 modules in the three npm package. There is actually a guide that explains how you can import them in your application:
https://threejs.org/docs/index.html#manual/en/introduction/Import-via-modules
For your particular case, it would look like so:
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls.js';
Notice that third-party packages like three-orbit-controls are not necessary anymore. When using the modules from the three package, it's guaranteed that you work with the latest (and supported) versions.
three.js R109
I know there's no one here to see this but I'm posting my solution if someone comes across this issue in the future. So here's what I figured out -
I haven't mentioned in my question that I was using a SSR (server side render) react framework called next.js. The import works perfectly fine with a pure react app. But in server side frameworks, the import related stuff should be done inside the useEffect (or componentDidMount) along with the rest of the threejs stuff. So I dynamically imported it like this -
let dynamicallyImportPackage = async () => {
let TrackballControls
await import('three/examples/jsm/controls/TrackballControls')
// you can now use the package in here
.then(module => {
TrackballControls = module.TrackballControls
})
.catch(e => console.log(e))
return TrackballControls
}
then, I used it in my useEffect like so -
let TrackbackControls = await dynamicallyImportPackage()
// controls
controls = new TrackbackControls(camera, container)
controls.minDistance = 5
controls.maxDistance = 250
Arigato Gosaimasu!

Resources