List of available components for the composable player.
<Viewer>
The Viewer component contains the Three.JS Canvas component within it, so it gathers everything related to the player under its wings. This is also where we specify the authentication for any of its children to allow access to the assets inside the ThreeKit Platform.
Warning!
In order to use the Viewer within a Next.JS implementation, we need to implement it within a separate component, which then has to be imported on the main page using the
dynamic
library. This is necessary in order to disable SSR completely, as the underlying architecture makes use of thewindow
object.See example in the Getting Started section.
Prop List
Name | Type | Status |
---|---|---|
auth | ThreekitAuthProps | Required |
resolver | {UnifiedResolver()} | Optional |
diagnostics | Boolean | Optional |
fgColor | String | Optional |
canvasProps | RenderProps | Optional |
children | React.ReactNode | Optional |
auth:
ThreekitAuthProps - Required
auth:
ThreekitAuthProps - RequiredThis is a required prop, as it needs to set the proper authentication for the Viewer components to communicate with the ThreeKit Platform.
resolver: {UnifiedResolver()}
- Optional
resolver: {UnifiedResolver()}
- OptionalThe resolver is used by the ThreeKit gltf exporter to generate each variant and send it to the player.
The recommended resolver is the following:
UnifiedResolver({
cacheScope: "v1",
optimize: false,
})
The cacheScope
allows us to specify which cached version of the assets should be loaded. When the assets have been updated on the platform, the string value for the cacheScope
should be updated manually at this time to a new value. The value itself can be anything, and it's up to the user.
diagnostics: Boolean
- Optional
diagnostics: Boolean
- OptionalChoose whether to generate diagnostics or not.
fgColor: String
- Optional
fgColor: String
- OptionalCSS colors for tinting the icons.
canvasProps:
RenderProps - Optional
canvasProps:
RenderProps - OptionalProps that can be passed to the Three.JS Canvas created by the Viewer.
Visit the official documentation.
children: React.ReactNode
- Optional
children: React.ReactNode
- OptionalChildren components to be passed to the Viewer.
Example
"use client";
import {
Scene,
Viewer,
UnifiedResolver,
} from "@threekit/react-three-fiber";
export default function Home() {
const auth = {
orgId: "YOUR_ORG_ID",
host: "preview.threekit.com",
publicToken: "YOUR_PUBLIC_TOKEN",
};
return (
<div style={{ width: "100%", height: "100%" }}>
<Viewer
auth={auth}
ui={true}
resolver={UnifiedResolver({
cacheScope: "v1",
optimize: false,
})}
>
<MyScene assetId={"b1571ea4-e7f3-400f-ba58-b875b382a4a5"} />
</Viewer>
</div>
);
}
"use client";
import dynamic from "next/dynamic";
const Player = dynamic(() => import("../components/Player"), { ssr: false });
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center p-24">
<div style={{ width: "673px", height: "550px", background: "#eee" }}>
<Player />
</div>
</main>
);
}
<MyScene>
The composable package does not currently contain a usable Scene component. However, here is an example of a Scene component that can be used to import scene assets, as well as changing cameras. You will find the Source Code below.
Prop List
Name | Type | Status |
---|---|---|
assetId | String | Required |
camera | THREE.PerspectiveCamera | Optional |
configuration | Configuration | Optional |
settings | ExporterSettings | Optional |
assetId: String
- Required
assetId: String
- RequiredThis represents the assetId of either a Scene or Catalog Item from the ThreeKit Platform.
camera: THREE.PerspectiveCamera
- Optional
camera: THREE.PerspectiveCamera
- OptionalThis prop can be used to set the specified camera object as the new active camera in the player. The Scene will copy its settings and apply them to the default camera of the player, retaining the original camera properties intact.
If connected to a state variable, this allows the user to change cameras automatically when the state changes.
configuration:
Configuration - Optional
configuration:
Configuration - OptionalThis prop can be used to request the gltf for a particular variant of the asset. If not specified, it will request the default configuration of the asset as specified in the Platform.
Connect this to a state variable that stores the configuration data, in order to update the Scene on every state change.
It is recommended to use the Composable Configurator to drive this configuration.
settings:
ExporterSettings - Optional
settings:
ExporterSettings - OptionalThese settings allow you to choose how the asset's contents should be exported and interpreted by the gltf exporter.
Source Code
Simply save this as a component file. This code makes use of the useScene hook in order to build our own Scene component.
"use client";
import { AssetProps, useScene } from "@threekit/react-three-fiber";
import * as THREE from "three";
import { useThree, useFrame, Object3DProps } from "@react-three/fiber";
import { forwardRef, useEffect, useState } from "react";
type MySceneProps = AssetProps &
Object3DProps & {
camera?: THREE.PerspectiveCamera | null;
};
function cloneCamera(camera: THREE.PerspectiveCamera | null) {
if (!camera) return null;
const worldPosition = new THREE.Vector3();
const worldRotation = new THREE.Quaternion();
camera.updateMatrixWorld(true);
camera.matrixWorld.decompose(
worldPosition,
worldRotation,
new THREE.Vector3()
);
const cameraClone = camera.clone();
cameraClone.position.copy(worldPosition);
cameraClone.rotation.setFromQuaternion(worldRotation);
cameraClone.updateMatrixWorld(true);
return cameraClone;
}
export default forwardRef(function MyScene(
props: MySceneProps,
ref: React.Ref<THREE.Object3D>
) {
const { camera, assetId, configuration, ...r3fProps } = props;
const threekitAsset = useScene({ assetId, configuration });
const r3f = useThree((r3f) => r3f);
const { domeLight } = threekitAsset.scene.userData;
const [defaultCamera, setDefaultCamera] =
useState<THREE.PerspectiveCamera | null>(
cloneCamera(threekitAsset.scene.userData.camera)
);
useEffect(() => {
if (!camera) return;
setDefaultCamera(cloneCamera(camera));
}, [camera]);
useEffect(() => {
if (!r3f.scene || !domeLight?.image) return;
const oldEnv = r3f.scene.environment;
r3f.scene.environment = domeLight.image;
return () => {
// undo our environment change, assuming it hasn't been overridden
if (r3f.scene.environment === domeLight.image) {
r3f.scene.environment = oldEnv;
}
};
}, [r3f, domeLight]);
useEffect(() => {
if (!defaultCamera) return;
defaultCamera.aspect = r3f.viewport.aspect;
defaultCamera.updateProjectionMatrix(); // only required initially. R3F only updates the aspect on canvas resize
const oldCam = r3f.camera;
if (r3f.camera === defaultCamera) return;
r3f.set({ camera: defaultCamera });
return () => {
if (r3f.camera === oldCam) {
// undo our camera change, assuming it hasn't been overridden
r3f.set({ camera: oldCam });
}
};
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [defaultCamera]);
useFrame(() => {
if (!domeLight?.image) return;
const intensity = domeLight.intensity ?? 1;
// unfortunately there's no global envMapFactor in three
r3f.scene?.traverse((obj) => {
const mesh = obj as THREE.Mesh;
if (mesh.isMesh) {
const materials = Array.isArray(mesh.material)
? mesh.material
: [mesh.material];
for (const mat of materials) {
if ("envMap" in mat && "envMapFactor" in mat) {
if (!mat.envMap) {
// only assign the intensity if the material doesn't have an envMap override
mat.envMapFactor = intensity;
}
}
}
}
});
});
return (
<>
{threekitAsset ? (
<primitive
object={threekitAsset.scene}
ref={ref}
castShadow
receiveShadow
{...r3fProps}
/>
) : null}
</>
);
});
Example
This component can then be imported into your Player component, and used as a child of the <Viewer>
component:
<Viewer
auth={auth}
ui={true}
resolver={UnifiedResolver({
cacheScope: "v1",
optimize: false,
})}
>
<MyScene assetId={"b1571ea4-e7f3-400f-ba58-b875b382a4a5"} />
</Viewer>
<Asset>
The <Asset>
component allows us to load the asset associated with a catalog item or model asset. It can be paired together with a Scene and other Assets within the Viewer.
The composable package does not currently contain a fully usable Asset component. However, here is an example of a Asset component that can be used to import model assets, as well as setting a ref
. You will find the Source Code below.
Props
Name | Type | Status |
---|---|---|
assetId | String | Required |
configuration | Configuration | Optional |
settings | ExporterSettings | Optional |
assetId: String
- Required
assetId: String
- RequiredThis represents the assetId of either a Catalog Item or a Model Asset from the ThreeKit Platform.
configuration:
Configuration - Optional
configuration:
Configuration - OptionalThis prop can be used to request the gltf for a particular variant of the asset. If not specified, it will request the default configuration of the asset as specified in the Platform.
Connect this to a state variable that stores the configuration data, in order to update the Model on every state change.
It is recommended to use the Composable Configurator to drive this configuration.
settings:
ExporterSettings - Optional
settings:
ExporterSettings - OptionalThese settings allow you to choose how the asset's contents should be exported and interpreted by the gltf exporter.
Source Code
Simply save this as a component file. This code makes use of the useAsset hook in order to build our own Asset component. The <Asset>
component included in "@threekit/react-three-fiber"
does not currently support ref
properly.
import { forwardRef } from "react";
import { type AssetProps, useAsset } from "@threekit/react-three-fiber";
import * as THREE from "three";
export default forwardRef(function Asset(
props: AssetProps,
ref: React.Ref<THREE.Object3D>
) {
const { assetId, configuration, ...r3fProps } = props;
const threekitAsset = useAsset({ assetId, configuration });
return (
<>
{threekitAsset ? (
<primitive
object={threekitAsset.scene}
caseShadows
receiveShadows
{...r3fProps}
ref={ref}
/>
) : null}
</>
);
});
Example
Please note how in this example we are loading the Asset in a separate <group>
container. The <group>
for the Asset component can be positioned to a specific location as shown here, or you can position it to the location of a specific node that is loaded from the Scene using the traverse method.
<Viewer
auth={auth}
ui={true}
resolver={UnifiedResolver({
cacheScope: "v1",
optimize: false,
})}
>
<OrbitControls />
<MyScene assetId={"b1571ea4-e7f3-400f-ba58-b875b382a4a5"} />
<group position={new THREE.Vector3(0, 0, 0)}>
<Asset assetId="3f8684ed-785f-4862-93bf-af7eeaaab961" />
</group>
</Viewer>;
<TurntableControls>
This component allows us to replicate the Node/Turntable Camera Control from the monolithic player. With this control mode we need to specify a target child that will receive the rotation around Y axis when the user drags to orbit. This will correspond to a horizontal drag. The vertical drag will continue to apply to the camera's X axis, resulting in a camera orbit vertically around the target child.
The focal target of the <TurntableControls>
is currently locked at the world (0, 0, 0) location. You will need to compensate for that within your Platform setup, or through <group>
components on the front-end.
Warning!
This control mode is not currently compatible with the Camera Switching method that uses the camera prop on the
<MyScene>
component.It is only compatible with camera switching performed through a configuration attribute that serves a separate gltf entirely.
Example
Please note that in this example we are applying a rotation to the whole set of assets loaded by the component. We have wrapped it in a component and provided to it a vertical shift along the Y axis in the negative direction, of -0.48 units to arrange the focal point of the spin to the desired location.
<TurntableControls>
<group position={new THREE.Vector3(0, -0.48, 0)} ref={groupRef}>
<MyScene assetId={"b1571ea4-e7f3-400f-ba58-b875b382a4a5"} />
</group>
</TurntableControls>