Appearance
Viz4D Viewer JavaScript API
API latest version v0.6
html
<script type="text/javascript" src="https://r.viz4d.com/api/viewer-api-0.6.js"></script>
Changelog
v0.6:
- Add new fn: reorderMtls, updateMtlsVisibility, getMtls
v0.5:
- Add new fn: getMapTransform, setMapTransform, getMtl, getMtlBySKU
v0.4:
- Add new fn: createMtl
v0.3:
Add new fn: goToTourStep, nextTourStep, prevTourStep, playTour, pauseTour, isTourPlaying, setTourAutoplay, isTourAutoplay, setTourSpeed, getTourSpeed, getCurrentTourStep, getTourStepCount
Add new events: tourTransitionStart, tourTransitionEnd, tourPlayStateChange
Rename V4DViewerAPI -> ViewerAPI
v0.2:
- Add new fn: toggleFullscreen, toggleMeasure, toggleHotspotVisible, toggleControlMode, toggleHelpDialog, toggleSectionDialog, toggleCaptureDialog, toggleXRDialog
v0.1:
- Initial release
Sample Code
html
<script type="text/javascript" src="https://r.viz4d.com/api/viewer-api-0.6.js"></script>
<!-- Normal iframe embed code -->
<iframe id="viewer-iframe" src="https://viz4d.com/viewer/_YOUR_SCENE_ID_" width="640" height="480" frameborder="0" allowfullscreen mozallowfullscreen="true" webkitallowfullscreen="true" allow="autoplay;fullscreen;xr-spatial-tracking;display-capture;gamepad" xr-spatial-tracking execution-while-out-of-viewport execution-while-not-rendered web-share></iframe>
<script>
const viewer = new ViewerAPI( document.getElementById("viewer-iframe"), {
btnXR: false,
btnCrossSection: false
} );
viewer.ready().then( async () => {
console.log("Begin select new mtl");
await viewer.selectMtl( "FLOOR", "Dark Wood Mtl" );
console.log("Finish select new mtl");
});
</script>
ViewerAPI Constructor
js
const viewer = new ViewerAPI ( HTMLIFrameElement, options = {
debug: false, // verbose logging
btnXR: true,
btnFullScreen: true,
btnCrossSection: true,
btnCapture: true,
btnMeasure: true,
btnHelp: true,
btnShare: true,
btnControlMode: true,
btnHotspot: true,
btnPlay: true,
btnInfo: true,
logo: true,
preloaderInfo: true,
loadingSign: true,
progressBar: true,
menuTour: true,
initialInfo: true, // Open scene info sidepanel at start, auto close after some seconds
initialHelp: true, // Show help dialog at start, auto close after some seconds
tourAutoplay: true,
autoVariant: false // if true, auto change mtl without needing user action, it will be stopped when user open any configHotspot for the first time
} );
API METHODS
play()
Returns: Promise<void>
pause( options )
Parameters:
options
object (optional)showPreloader
boolean - Show preloader screen after pause (default: true)
Returns: Promise<void>
isPlaying()
Returns: Promise< boolean >
selectMtl( mtlSetID, mtlName, options )
Parameters:
mtlSetID
stringmtlName
stringoptions
object (optional)transition
number - Transition duration in ms (default: 2000)
Returns: Promise<void>
Description: Select a material from existing materials in mtlSet
INFO
This promise resolves in two scenarios:
- When the transition animation fully completes
- When a new
selectMtl()
call is made for the same mtlSet before the current transition finishes. In this case, the current transition will be interrupted and the promise will resolve early.
selectMtlBySKU( mtlSetID, mtlSKU, options )
Description: Behavior similar to selectMtl()
, but select by SKU instead of name.
getSelectedMtl( mtlSetID )
Parameters:
mtlSetID
string
Returns: Promise< { name, sku, content } >
Description: Get currently selected material in mtlSet
getMtl( mtlName )
Parameters:
mtlName
string
Returns: Promise< { name, sku, content } >
Description: Get material info by name
getMtlBySKU( mtlSKU )
Parameters:
mtlSKU
string
Returns: Promise< { name, sku, content } >
Description: Get material info by SKU.
Can be useful in case where you mainly refer to mtl by SKU, but some fn requires mtlName, so you can use getMtlBySKU to get the name of mtl
Example:
js
// get mtlName from SKU
const mtlInfo = await viewer.getMtlBySKU("MTL_SKU_HERE");
const mtlName = mtlInfo.name;
// pass mtlName to use in other fn
const diffuseTransform = await viewer.getMapTransform( mtlName, "diffuse" );
getMtls( mtlSetID )
Parameters:
mtlSetID
string
Returns: Promise< Array[ { name: string, sku: string|null, visible: boolean, content: string|null }, ... ] > - example: [ {name: "Mtl 1", sku: null, visible: true, content: ""}, {name: "Mtl 2", sku: null, visible: true, content: ""} ]
Description: Get all materials of mtlSet in order. This fn mainly used with reorderMtls()
to re-order materials, or updateMtlsVisibility()
to update visibility of materials.
reorderMtls( mtlSetID, mtlList )
Parameters:
mtlSetID
stringmtlList
Array[ {name, sku}, ... ] - should be array of MtlInfo get fromgetMtls()
, only the order of mtl inside array can be changed, don't add or remove any mtl from array. Requirename
orsku
property for each mtl in array.
Returns: Promise<void>
Description: Use to reorder materials in mtlSet.
Common workflow:
- Get materials array from
getMtls()
- Sort materials array by some criteria
- Call
reorderMtls()
with sorted materials array
Re-order materials examples:
js
// Sort materials by name alphabetically
const mtlList = await viewer.getMtls( "MTL_SET_ID" );
mtlList.sort( (mtlA, mtlB) => {
return mtlA.name.localeCompare(mtlB.name);
});
await viewer.reorderMtls( "MTL_SET_ID", mtlList );
js
// Move materials with "metal" in their sku to the top of the list
const mtlList = await viewer.getMtls( "MTL_SET_ID" );
mtlList.sort( (mtlA, mtlB) => {
const isMetalA = mtlA.sku && mtlA.sku.toLowerCase().includes("metal");
const isMetalB = mtlB.sku && mtlB.sku.toLowerCase().includes("metal");
if (isMetalA && !isMetalB) return -1;
if (!isMetalA && isMetalB) return 1;
return 0;
});
await viewer.reorderMtls( "MTL_SET_ID", mtlList );
updateMtlsVisibility( mtlSetID, visibilityChanges )
Parameters:
mtlSetID
stringvisibilityChanges
Array[ {name, visible}, ... ] - Array of objects specifying visibility changes:js[ // No need to provide all materials of mtlSet here, only include the ones you want to change visiblity. // the order in this array doesn't matter, it does not affect the order of materials in mtlSet. { name: "Material Name", visible: true }, // either name or sku must be provided. If both are provided, name will be preferred because name is guaranteed to be unique, while sku may not be unique or be null. { sku: "SKU_123", visible: false } ]
Returns: Promise<void>
Description: Update visibility of specified materials in mtlSet. Each entry in visibilityChanges
can specify a material by name or SKU and the desired visibility state.
updateMtlsVisibility examples:
js
// Hide material with SKU "SKU_123"
await viewer.updateMtlsVisibility( "MTL_SET_ID", [ { "sku": "SKU_123", "visible": false } ] );
js
// Hide all materials with "wood" in their name
const mtlList = await viewer.getMtls( "MTL_SET_ID" );
const visibilityChanges = mtlList
.filter(mtl => mtl.name.toLowerCase().includes("wood")) // filter array to only include materials with "wood" in their name
.map(mtl => ({ name: mtl.name, visible: false })); // map array to only include the name and set visible to false
await viewer.updateMtlsVisibility( "MTL_SET_ID", visibilityChanges );
js
// Only show materials with "metal" in their sku, hide all other materials
const mtlList = await viewer.getMtls( "MTL_SET_ID" );
const visibilityChanges = mtlList.map(mtl => ({
name: mtl.name,
visible: mtl.sku && mtl.sku.toLowerCase().includes("metal")
}));
await viewer.updateMtlsVisibility( "MTL_SET_ID", visibilityChanges );
createMtl( mtlDef, mtlSetID )
Parameters:
mtlDef
objectmtlSetID
string - (optional) target mtlSet to add the new mtl
Returns: Promise<void>
Promise resolve when all textures of this new mtl are loaded.
Description: Create a new material from mtlDef and add to mtlSet
mtlDef Structure
Note:
All properties are optional (except "name"). If any properties are not provided, default value like below will be used, so you don't need to provide all properties.
js
{
name: string, // REQUIRED, must be unique in scene, otherwise Promise will reject
sku: string, // default null
content: string, // Markdown description of the material, default null
diffuse: {
color: [1, 1, 1], // [r, g, b], range from 0 to 1
map: { // default null
url: "/url/to/map.jpg", // .jpg / .png / .webp
repeat: [1, 1], // [x, y]
offset: [0, 0], // [x, y]
rotation: 0 // in degree
},
mapAmount: 1, // range 0-1
mapBrightness: 1
},
reflect: {
color: [0, 0, 0],
map: {
url: "/url/to/map.jpg",
repeat: [1, 1],
offset: [0, 0],
rotation: 0
},
mapAmount: 1
},
glossiness: {
factor: 0, // range 0-1
map: {
url: "/url/to/map.jpg",
repeat: [1, 1],
offset: [0, 0],
rotation: 0
},
mapAmount: 1,
mapInvert: false // default false. If true, invert glossiness map, essentially treat it as roughness map. Only affect map, does not affect glossiness.factor
},
ior: {
factor: 1.5, // range 1 - 1000
map: {
url: "/url/to/map.jpg",
repeat: [1, 1],
offset: [0, 0],
rotation: 0
},
mapAmount: 1
},
metalness: {
factor: 0, // range 0-1
map: {
url: "/url/to/map.jpg",
repeat: [1, 1],
offset: [0, 0],
rotation: 0
},
mapAmount: 1
},
opacity: {
factor: 1, // range 0-1
map: {
url: "/url/to/map.jpg",
repeat: [1, 1],
offset: [0, 0],
rotation: 0
},
mapAmount: 1
},
transparency: {
factor: 0, // range 0-1
map: {
url: "/url/to/map.jpg",
repeat: [1, 1],
offset: [0, 0],
rotation: 0
},
mapAmount: 1
},
emissive: {
factor: 1, // range 0-1
color: [0, 0, 0],
map: {
url: "/url/to/map.jpg",
repeat: [1, 1],
offset: [0, 0],
rotation: 0
},
mapAmount: 1
},
normal: {
map: {
url: "/url/to/map.jpg",
repeat: [1, 1],
offset: [0, 0],
rotation: 0
},
mapAmount: 1 // normal scale, can be negative (similar to inverted Y channel)
},
bump: {
map: {
url: "/url/to/map.jpg",
repeat: [1, 1],
offset: [0, 0],
rotation: 0
},
mapAmount: 1 // bump scale, can be negative
},
clearcoat: {
factor: 0, // range 0-1
glossiness: 0, // range 0-1
},
environment: {
factor: 1, // range 0-1
localMode: true // if false, treat the reflection probe as infinite far away
},
sheen: {
enable: false,
color: [0, 0, 0],
glossiness: 0, // range 0-1
},
side: 1, // 1: front-side, -1: back-side, 2: double-side
alphaCutoff: 0, // range 0-1
depthWrite: true,
flatShading: false,
excludeFromLightIntensity: false,
excludeFromLightReflection: false,
polygonOffset: {
enable: false,
factor: 0,
}
}
createMtl Examples:
js
// Create mtl with red diffuse color
await viewer.createMtl({
name: "Red Mtl",
sku: "SKU_123",
content: "This is a **Markdown** description of the material",
diffuse: {
color: [1, 0, 0],
}
}, "MTL_SET_ID");
// Select new mtl right afterward
await viewer.selectMtl( "MTL_SET_ID", "Red Mtl" );
js
// Create mtl with diffuse map
await viewer.createMtl({
name: "Mtl with diffuse map",
diffuse: {
map: {
url: "https://example.com/diffuse.jpg",
repeat: [2, 2], // custom repeat
},
mapAmount: 0.9
},
reflect: {
color: [ 1, 1, 1 ]
},
glossiness: {
factor: 0.9
}
}, "MTL_SET_ID");
getMapTransform( mtlName, mapSlot )
Parameters:
mtlName
stringmapSlot
string - Valid values for mapSlot:"diffuse"
,"opacity"
,"transparency"
,"emissive"
,"glossiness"
,"reflect"
,"ior"
,"metalness"
,"normal"
,"bump"
,"any"
Returns: Promise< { repeat: [number, number], offset: [number, number], rotation: number } >
This fn will resolve null if the specified map slot is empty (doesn't have a map).
MapSlot "any"
mean that it will get transform of one of any map this mtl using (prioritize diffuse map if available). If mtl doesn't have any map, fn will resolve null.
Examples:
js
const diffuseTransform = await viewer.getMapTransform( "MTL_NAME", "diffuse" );
if ( diffuseTransform != null ) {
console.log(diffuseTransform);
// logged values: { repeat: [1,1], offset: [0,0], rotation: 0 }
}
setMapTransform( mtlName, mapSlot, transform )
Parameters:
mtlName
stringmapSlot
string - Valid values for mapSlot:"diffuse"
,"opacity"
,"transparency"
,"emissive"
,"glossiness"
,"reflect"
,"ior"
,"metalness"
,"normal"
,"bump"
,"all"
transform
objectrepeat
[number, number] - [x, y] (optional)offset
[number, number] - [x, y] (optional)rotation
number - in degree (optional)
Returns: Promise<void>
Examples:
js
// set repeat of diffuse map to 2x2
await viewer.setMapTransform( "MTL_NAME", "diffuse", {
repeat: [3, 3]
} );
// set repeat and offset of all map (note that lightmap is not affected)
await viewer.setMapTransform( "MTL_NAME", "all", {
repeat: [3, 3],
offset: [0.5, 0.5]
} );
// x2 existing repeat of diffuse map
const existingTransform = await viewer.getMapTransform( "MTL_NAME", "diffuse" );
if ( existingTransform != null ) {
await viewer.setMapTransform( "MTL_NAME", "diffuse", {
repeat: [ existingTransform.repeat[0] * 2, existingTransform.repeat[1] * 2 ]
} );
}
openHotspot( hotspotName, options )
Parameters:
hotspotName
stringoptions
object (optional)goToView
boolean - Go to the hotspot view after open (default: true)transition
number - Transition duration in ms (default: 3000)
Returns: Promise<void>
Description: Open a hotspot. Note: This promise resolve when transitionEnd
openInfo()
Returns: Promise<void>
Description: Open the scene info popup (title, description)
isPopupOpen()
Returns: Promise< boolean >
Description: Check if any popup is currently opened.
INFO
Only 1 popup can be opened at one time. Open a new popup will auto close the current popup.
closePopup()
Returns: Promise<void>
Description: Close the currently opened popup (hotspot / info)
toggleControlMode()
Returns: Promise<void>
Description: Switch between Orbit and Walk control modes
toggleMeasure()
Returns: Promise<void>
Description: Toggle measure tool
toggleHotspotVisible()
Returns: Promise<void>
Description: Toggle visibility of all hotspots
toggleHelpDialog()
Returns: Promise<void>
toggleSectionDialog()
Returns: Promise<void>
toggleCaptureDialog()
Returns: Promise<void>
toggleXRDialog()
Returns: Promise<void>
playTour()
Returns: Promise<void>
Description: Start or resume tour autoplay
Note: Tour will be paused when user interact with scene (move, rotate, open hotspot...)
pauseTour()
Returns: Promise<void>
isTourPlaying()
Returns: Promise<boolean>
nextTourStep( options )
Parameters:
options
object (optional)transition
number - Override transition duration (in ms)force
boolean - if true, tour transition will be uninterruptible by user interaction (default: false)
Returns: Promise<void>
Description: Go to next step in the tour.
Promise resolve when transition end. Promise reject if transition is interrupted by user interaction.
When tour is at last step, nextTourStep
will loop back to first step.
prevTourStep( options )
Parameters:
options
object (optional)transition
number - Override transition duration (in ms)force
boolean - Tour transition will be uninterruptible by user interaction if true (default: false)
Returns: Promise<void>
Description: Go to previous step in the tour.
Promise resolve when transition end. Promise reject if transition is interrupted by user interaction.
When tour is at first step, prevTourStep
will loop back to last step.
goToTourStep( index, options )
Parameters:
index
numberoptions
object (optional)transition
number - Override transition duration (in ms)force
boolean - Tour transition will be uninterruptible by user interaction if true (default: false)
Returns: Promise<void>
Description: Go to a specific step in the tour.
Promise resolve when transition end. Promise reject if transition is interrupted by user interaction.
getCurrentTourStep()
Returns: Promise<number>
Description: Get the index of the current step in the tour
getTourStepCount()
Returns: Promise<number>
getTourSpeed()
Returns: Promise<number>
setTourSpeed( multiplier )
Parameters:
multiplier
number - Speed multiplier (default: 1)
Returns: Promise<void>
Description: Set the speed multiplier, all transition and wait time of tour will be multiplied by this value.
on( eventName, callback )
Returns: void
Description: Add a callback to an event
off( eventName, callback )
Returns: void
Description: Remove a callback from an event
once( eventName, callback )
Returns: void
Description: Add a callback to an event, but it will be removed after the first time it is triggered.
LOADING STATES
Viewer has 3 loading states, sorted in chronological order:
initialized
>> ready
>> loaded
initialized()
Returns: Promise<void>
Description: This promise resolve when the Viewer finish initialization and begin to loading assets.
List of fn available before initialized
: on, off, once, initialized, ready, loaded, isPlaying, pause, play
Note
If Viewer has autoplay off (?autoplay=0
) then initialized
only trigger after the Viewer play.
ready() Promise<void>
This promise resolve when scene has loaded minimum necessary data of all objects and materials, but not fully loaded, which means low LOD of meshes/textures are added to scene, but higher LOD are still loading progressively.
INFO
All api fn are available after this ready
state.
loaded() Promise<void>
This promise resolve when all data of scene is fully loaded.
API EVENTS
Events Usage Example:
javascript
viewer.on("playStateChange", (isPlaying) => {
console.log("Viewer is now:", isPlaying ? "playing" : "paused");
});
viewer.on("hotspotOpen", ({ name, sku, content }) => {
console.log(`Hotspot "${name}" opened`);
});
playStateChange
Callback Parameters:
isPlaying
boolean - Current play state of the viewer
hotspotOpen
Callback Parameters: object
typescript
{
name: string, // Name of the opened hotspot
sku: string, // SKU of the opened hotspot
content: string // Markdown content of the hotspot
}
hotspotClose
Callback Parameters: void
selectMtlStart
Callback Parameters: object
typescript
{
mtlSetID: string, // ID of the material set being changed
name: string // Name of the new material
}
selectMtlEnd
Callback Parameters: object
typescript
{
mtlSetID: string, // ID of the material set that was changed
name: string // Name of the applied material
}
tourTransitionStart
Callback Parameters: object
typescript
{
fromStep: number, // Index of the previous step
toStep: number, // Index of the target step
transition: number // Transition duration in ms
}
tourTransitionEnd
Callback Parameters: object
typescript
{
fromStep: number, // Index of the starting step
currentStep: number, // Index of the current step
completed: boolean // Whether transition completed successfully or interrupted
}
tourPlayStateChange
Callback Parameters: object
typescript
{
isTourPlaying: boolean, // Whether tour is currently playing
currentStep: number // Current step index
}