I am generating several ShapeDiver models in my front-end. The idea is to remove one window and then generate a new one.
This is how I generate a new window: api = await new window.SDVApp.ParametricViewer(settings);
If I rerun this command it seems it just pushes new canvases to the DOM without removing the previous one.
So, my questions is, how to remove safely one view instance from the DOM? Basically I am supplying a new ticket to the viewer, so I want to render this new model instead.
Following code example shows you how to load and destroy the viewer.
In the CodePen console (bottom left button to open) you can type in loadViewer to load the viewer and destroyViewer to destroy it again. As you can see, this process can be repeated anytime.
It seems it was a WebGL issue in Chrome, I tried in Safari and works ok.
Thanks again, this will be the solution, I just need to implement it to my React app.
@pavol I implemented your suggestion into a cleanup function in a React hook, it might help others too who is using React 16+. There might be other solutions too, but this works for me.
My RenderSDScene function renders the SDViewer when the component is mounted or the ticket props is changing, in my case.
useEffect(() => {
renderSDScene()
return () => {
let result = api.viewports.get();
api.viewports.destroy(result.getViewportRuntimeId());
delete window.api
console.log("SDViewer component was unmounted un cleaned up!")
}
}, [props.ticket])
I’ve found that destroying viewports does indeed help with unmounting ShapeDiver in the DOM but, it helps to take things further. Simply destroying the viewport can lead to a webGL frame buffer warning plus, if a user is rapidly opening and closing your ShapeDiver component, error messages can appear. Despite the console messages, things may appear to function as expected.
I worry about performance degradation in webGL because of frame buffer issues, lingering contexts… and, if a user is forcing rapid mounts and unmounts, things can break.
I use the following to keep my console and conscience clean:
const useCloseActiveViewports = (api) => {
const viewports = api.viewports.get();
// kill all viewports
// don't want any issues building up webGL contexts
if (Array.isArray(viewports)) {
let runtimeIDs = [];
viewports.forEach((viewport) => {
runtimeIDs.push(viewport.getViewportRuntimeId());
});
runtimeIDs.forEach((runtimeID) => {
viewer.viewports.destroy(runtimeID);
});
} else {
const runtimeID = viewports.getViewportRuntimeId();
viewer.viewports.destroy(runtimeID);
}
}
const useUnmountShapediver = async(api) => {
if (api) {
// don't invite error messages by abruptly killing any processes
await api.scene.pause();
// set show to false to avoid viewport ghost when creating a new viewport
// relevant if you're reusing the api object
await api.updateSettingAsync("scene.show", false);
// handle the api object
whateverYouWantToDoWithTheApiObjectFunction();
// destroy all active viewports
// ...you could potentially restore a viewport via:
// .getExtension('WEBGL_lose_context').restoreContext();
// but this is much more complex...
// destroying enables you to use ShapeDiver's api function
// viewport.destroy to "clean-up"
useCloseActiveViewports(api);
// Let the parent know child unmount processes are done
return unmountCallbackFunction();
}
}
Note: I reuse my api objects - simply destroying them may avoid things breaking on very rapid mount, unmount, mount, unmount… processes but, I’ve also noticed that ShapeDiver may not even pick up on a React re-render if it’s fast. Also, I renamed some things for the forum so there could be a typo up there.