A web application that demonstrates how to import and place glTF 3D models on a map using ArcGIS JavaScript API.
- 3D Model Import: Supports importing glTF models (Shiba and Tower)
- Interactive Placement: Place models on the map using mouse clicks
- Real-time Updates: Automatic UI updates based on model selection
- Multiple Model Support: Choose between different 3D models
- Ground Elevation: Models are automatically placed on ground level
- Node.js
- Vite
npm create vite@latestFollow the instructions on screen to initialize the project.
npm install @arcgis/map-components<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
<title>Import glTF models</title>
</head>
<body>
<arcgis-scene item-id="414a28cfca7a471180e8e952cf14c60f">
<arcgis-zoom position="top-left"></arcgis-zoom>
<arcgis-navigation-toggle position="top-left"></arcgis-navigation-toggle>
<arcgis-compass position="top-left"></arcgis-compass>
<div id="paneDiv" class="esri-widget">
<p>Select a symbol and place it in the scene:</p>
<button id="shiba" class="esri-button">Shiba</button><br />
<button id="tower" class="esri-button">Tower</button>
</div>
</arcgis-scene>
<script type="module" src="./src/main.ts"></script>
</body>
</html>@import "https://js.arcgis.com/calcite-components/3.2.1/calcite.css";
@import "https://js.arcgis.com/4.32/esri/themes/light/main.css";
@import "https://js.arcgis.com/4.32/map-components/main.css";
html,
body {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#paneDiv {
padding: 10px;
max-width: 200px;
background-color: rgba(255, 255, 255, 0.8);
font-size: 1.1em;
}- Import the required modules
import "./style.css";
import "@arcgis/map-components/components/arcgis-scene";
import "@arcgis/map-components/components/arcgis-zoom";
import "@arcgis/map-components/components/arcgis-navigation-toggle";
import "@arcgis/map-components/components/arcgis-compass";
import GraphicsLayer from "@arcgis/core/layers/GraphicsLayer";
import SketchViewModel from "@arcgis/core/widgets/Sketch/SketchViewModel";
import SceneView from "@arcgis/core/views/SceneView";- Get the scene view
const webScene: HTMLArcgisSceneElement | null = document.querySelector("arcgis-scene");
if (!webScene) {
throw new Error("Scene element not found");
}
await webScene.viewOnReady();
// Create the SceneView and attach it to the container
const view: SceneView | null = webScene.view;
if (!view) {
throw new Error("View not found");
}- Create a GraphicsLayer, get the burrons and create a sketch view model
// Create a GraphicsLayer for 3D models with ground elevation
const graphicsLayer: GraphicsLayer | null = new GraphicsLayer({
elevationInfo: { mode: "on-the-ground" }, // Models will be placed on ground level
});
view.map?.add(graphicsLayer); // Add the graphics layer to the map
// Get UI elements for model selection
const shibaButton: HTMLButtonElement | null = document.querySelector("#shiba");
if (!shibaButton) {
throw new Error("Shiba button not found");
}
const towerButton: HTMLButtonElement | null = document.querySelector("#tower");
if (!towerButton) {
throw new Error("Tower button not found");
}
// Create SketchViewModel for placing 3D models
const model = new SketchViewModel({
layer: graphicsLayer,
view: view,
});- Handle model placement
shibaButton.addEventListener("click", () => {
// Configure the model symbol for Shiba
model.pointSymbol = {
type: "point-3d",
symbolLayers: [
{
type: "object",
resource: {
href: "./shiba.glb", // Path to Shiba model
},
},
],
};
// Start model placement mode
model.create("point");
// Reset button states
deactivateButton();
// Highlight the selected button
shibaButton.classList.add("esri-button-secondary");
});
// Handle Tower model placement
towerButton.addEventListener("click", () => {
// Configure the model symbol for Tower
model.pointSymbol = {
type: "point-3d",
symbolLayers: [
{
type: "object",
resource: {
href: "./tower.glb", // Path to Tower model
},
},
],
};
// Reset button states
deactivateButton();
// Start model placement mode
model.create("point");
// Highlight the selected button
towerButton.classList.add("esri-button-secondary");
});- Listen for model creation events
// Listen for model creation events
model.on("create", (event) => {
if (event.state === "complete") {
// Update the model with the created graphic
model.update(event.graphic);
// Reset button states
deactivateButton();
}
});- Add a function to reset button states
function deactivateButton() {
// Get all buttons with the esri-button class
const elements = Array.prototype.slice.call(
document.getElementsByClassName("esri-button")
);
// Remove the secondary button class from all buttons
elements.forEach((element) => {
element.classList.remove("esri-button-secondary");
});
}- Add the UI element to the scene view
view.ui.add("paneDiv", "top-right");- For development, run:
npm run devThe application can then be run on https://localhost:5173
- For production, run:
npm run build
npm run preview