The intended response from the code below is to load a 3d object into a scene based on buttons clicked by the user. The first time an object is selected, it is pulled from a server, stored into a cache, and added to the scene. If a user selects a different 3d object, it will remove the 3d object currently in the scene and load the new object. If the user clicks the same object again, it will remove the object from the scene.
I’m currently having a strange bug where if I first click an object and it loads, and then I select a different object, it will remove the first object, put the 2nd object into the scene (which I verify is in the scene put printing scene to the console) but it isn’t visible. Also, if I load an object, then remove it, then try to load the same object to the screen, it isn’t rendered to the screen.
In all cases, when I look at the objects in the scene, it says that the object is there and is visible. After it doesnt show up on the 2nd load, when I click different objects, they load and are removed from the scene appropriately.
Note: I’m new to asking for help like this so unsure how much code to post. And since I’m really not sure where the issue is. No errors are thrown when executing the code, and as stated, when I print the scene to console, it states the object is in the scene, you just can’t see it
var FBXLoader = require('three-fbx-loader');
class ModelBuilder extends Component {
state = {
objectNames: ...
cache: {
...Cached3dObjectCategories
},
current: {
Current3dObjects...
}
}
setStateHandler =(category, selection, downloadedFile, fromDownload) => {
console.log("4");
var prevName = this.state[category];
//if clicking already selected object
if(this.state[category] !== selection) {
console.log("5");
if(fromDownload){
console.log("6");
const url = window.URL.createObjectURL(downloadedFile);
this.setState({
...this.state,
[category]: selection,
cache: {
...this.state.cache,
[category] : {
...this.state.cache[category],
[selection]: url
}
},
current: {
...this.state.current,
[category]: url
},
}, update => {
this.updateScene(category, selection, prevName);
})
} else {`enter code here`
this.setState({
...this.state,
[category]: selection,
current: {
...this.state.current,
[category]: this.state.cache[category][selection]
}
}, update => {
this.updateScene(category,selection, prevName);
})
}
} else {
this.removeObjectFromScene(selection);
this.setState({
...this.state,
[category]: '',
current: {
...this.state.current,
[category]: null
}
}, update => {
console.log(this.state);
})
}
}
downloadObjectFromDatabase = (category, selection) => {
console.log("2");
return new Promise((resolve, reject) => {
firebase.storage().ref('/Models/' + category + '/' + selection + '.fbx')
.getDownloadURL()
.then(url => {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(event) {
resolve(xhr.response);
};
xhr.open('GET', url);
xhr.send();
})
.catch(error => {
reject(error);
})
});
}
isObjectInCache = (category, selection) => {
const cacheCategory = this.state.cache[category];
let inCache = false;
for (var object in cacheCategory) {
if(object === selection) {
inCache = true;
break;
}
}
return inCache;
}
async updateSelectionHandler(category, selection, setStateHandler, updateScene) {
const alreadyInCache = this.isObjectInCache(category, selection);
if(!alreadyInCache){
console.log("1");
try {
const downloadedFile = await this.downloadObjectFromDatabase(category, selection);
console.log("3");
setStateHandler(category, selection, downloadedFile, true, updateScene);
} catch (error) {
console.log(error);
}
} else {
setStateHandler(category, selection, this.state.current[category], false, updateScene);
}
}
constructor(props) {
super(props)
this.start = this.start.bind(this)
this.stop = this.stop.bind(this)
this.animate = this.animate.bind(this)
this.setStateHandler = this.setStateHandler.bind(this)
this.loadObjectFromCache = this.loadObjectFromCache.bind(this);
}
componentDidMount() {
this.init();
this.updateScene();
}
componentWillUnmount() {
this.stop()
this.mount.removeChild(this.renderer.domElement)
}
start = () => {
if (!this.frameId) {
this.frameId = requestAnimationFrame(this.animate)
}
}
stop = () => {
cancelAnimationFrame(this.frameId)
}
animate = () => {
this.renderScene()
this.frameId = window.requestAnimationFrame(this.animate)
}
renderScene = () => {
this.renderer.render(this.scene, this.camera)
}
removeObjectFromScene = (objectName) => {
var selectedObject = this.scene.getObjectByName(objectName);
this.scene.remove(selectedObject);
}
loadObjectFromCache = (url) => {
return new Promise((resolve, reject) => {
this.loader.load(
url,
(object) => {
object.traverse( function ( child ) {
if( child.material ) {
child.material = new THREE.MeshStandardMaterial( {
color: 0xC0C0C0 } );
}
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
});
object.castShadow = true;
resolve(object);
},
null,
(error) => {
reject(error);
}
)
});
}
async updateScene (category, selection, prevName) {
console.log("7");
if(selection && this.state.current[category] !== null){
try {
console.log("8");
const object = await
this.loadObjectFromCache(this.state.current[category]);
object.scale.set( .02, .02, .02 );
if(prevName !== ''){
this.removeObjectFromScene(prevName);
}
object.name = selection;
object.position.y = -1;
this.scene.add(object);
console.log(this.scene);
} catch (error) {
console.log(error);
}
};
console.log(this.state);
}
init = () => {
this.THREE = THREE;
this.loader = new FBXLoader();
const width = this.mount.clientWidth
const height = this.mount.clientHeight
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
75,
width / height,
0.1,
1000
)
//offset view so model shifts to left side of the screen
camera.setViewOffset(width * 1.3, height * 1.3, width * .3, height * .1, width, height );
camera.position.z = 3
const renderer = new THREE.WebGLRenderer({ antialias: true })
//enable orbit controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enabled = true;
controls.enablePan = false;
controls.minDistance = 2.0;
controls.maxDistance = 3.0;
// How far you can orbit vertically, upper and lower limits.
// Range is 0 to Math.PI radians.
controls.minPolarAngle = 0; // radians
controls.maxPolarAngle = Math.PI / 2; // radians
//changes the size of screen when browser resized
window.addEventListener('resize', function()
{
var width = window.innerWidth;
var height = window.innerHeight;
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
});
//Scene Lighting
var ambientLight = new THREE.AmbientLight(0xFFFFFF, .2);
var keyLight = new THREE.DirectionalLight(new THREE.Color('hsl(30, 100%, 75%)'), 1.0);
keyLight.position.set(-100, 0, 100);
var fillLight = new THREE.DirectionalLight(new THREE.Color('hsl(240, 100%, 75%)'), 0.75);
fillLight.position.set(100, 0, 100);
var backLight = new THREE.DirectionalLight(0xffffff, 1.0);
backLight.position.set(100, 0, -100).normalize();
scene.add(ambientLight);
scene.add(keyLight);
scene.add(fillLight);
scene.add(backLight);
//add the skybox
var geometry = new THREE.CubeGeometry(100, 100, 100);
var cubeMaterials = [
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load(px), side: THREE.DoubleSide}),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load(nx), side: THREE.DoubleSide}),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load(py), side: THREE.DoubleSide}),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load(ny), side: THREE.DoubleSide}),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load(pz), side: THREE.DoubleSide}),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load(nz), side: THREE.DoubleSide}),
];
var cubeMaterial = new THREE.MeshFaceMaterial(cubeMaterials);
var cube = new THREE.Mesh(geometry, cubeMaterial);
scene.add(cube);
renderer.setClearColor('#000000')
renderer.setSize(width, height)
this.scene = scene
this.camera = camera
this.renderer = renderer
this.mount.appendChild(this.renderer.domElement)
this.start()
return scene;
}