Example features
- Init viewer
- Loading model
- Camera zoom and rotate
As mentioned, the React examples use the react-three library, which is popular in the community. Let's use the Base Viewer as an example to consider the general concept of working with MTK Web.
Basic concepts
For convenience, all functions related to loading and displaying the model, working with the scene, camera and light are placed in the so-called Viewer.
The viewer has a manager, which, in turn, contains all the functions for managing objects on the scene.
The model on the stage is presented in the form of a tree. Tree nodes - model objects - may contain some data, such as material, state, name and etc.
Initialize viewer and load model
First, of course, you will need the model file itself. In our examples, loading the model from both the archive and the folder is implemented. To do this, a react-hook called 'useModelLoader' is used. The code block below shows a way to work with this hook.
// BaseViewer
function BaseViewer() {
const [viewer] = useState(new BaseModelViewer()); // save viewer object in state
const {
currentFile,
filesList,
gridFadeDistance,
gridPosition,
lightPosition,
onMTKWEBFileSelected,
onMTKWEBFolderSelected,
zUpEuler,
} = useModelLoader(viewer);
return (
<>
<aside style={{ position: 'fixed', zIndex: 1, width: 300 }}>
<UploadModel onArchiveSelected={onMTKWEBFileSelected} onFolderSelected={onMTKWEBFolderSelected} />
</aside>
<Canvas shadows frameloop="demand">
<color attach="background" args={['aliceblue']} />
<directionalLight color="white" intensity={1} position={lightPosition} /> // Adjust light
<ReactModelViewer viewer={viewer} /> // viewer will init in this component
<SpaceGrid position={gridPosition} rotation={zUpEuler} fadeDistance={gridFadeDistance} /> // Add grid
</Canvas>
</>
);
}
The "UploadModel" component is just a UI.
// ReactModelViewer
function ReactModelViewer<
TreeNodeDataType extends StructureManagerTreeNodeData,
StructureManagerType extends StructureManager<TreeNodeDataType> = StructureManager<TreeNodeDataType>,
>({ viewer }: ReactModelViewerProps<TreeNodeDataType, StructureManagerType>) {
const camera = useThree((state) => state.camera);
const renderer = useThree((state) => state.gl);
const scene = useThree((state) => state.scene);
const invalidate = useThree((state) => state.invalidate);
useEffect(() => {
viewer.init(scene, renderer, camera, () => invalidate());
return () => {
viewer.dispose();
};
}, [scene, renderer, camera, invalidate]);
useFrame((_state: RootState, delta: number) => {
viewer.cameraControls.update(delta);
});
return null;
}
Viewer
Each example has its own class inherited from BaseViewer. Inside this class, you only need to define a manager
class BaseModelViewer extends BaseViewer<BaseViewerStructureManagerTreeNodeData, BaseViewerStructureManager> {
protected _structureManager = new BaseViewerStructureManager();
}
Structure Manager
Each manager is inherited from a StructureManager, to which a type of data is passed as generic. Inside this class, you only need to define a model loading function. An example of this is below.
class BaseViewerStructureManager extends StructureManager<BaseViewerStructureManagerTreeNodeData> {
async loadModel(model: mtk.Model) {
this.clear();
const fileSceneObject = new Group();
const fileNode = new TreeNode(model.name || 'Unnamed model', 'file', { sceneObject: fileSceneObject });
fileSceneObject.name = fileNode.text;
const converter = new ModelConvertor(fileNode);
model.accept(converter);
this.addRoot(fileNode);
this.showAllNodes();
this.modelSceneObject.add(fileSceneObject);
}
}
Run the application
Now, you just need to run the react application.
Base Viewer