I’m new to using webxr with threejs and I’m doing AR experiences. I’ve been trying for 2 days to do a functionality that is in the examples on the threejs page three.js examples
Basically it’s doing a dragging with a GLTF model, I’ve been looking for information on the internet but I couldn’t find the solution. I’ve done things in different ways but I still can’t move the object.
This is the code
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { XRButton } from "three/addons/webxr/XRButton.js";
import { XRControllerModelFactory } from "three/addons/webxr/XRControllerModelFactory.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
let container;
let camera, scene, renderer;
let controller1, controller2;
let controllerGrip1, controllerGrip2;
let raycaster;
const intersected = [];
let controls, group;
init();
function init() {
container = document.createElement("app");
document.body.appendChild(container);
scene = new THREE.Scene();
scene.background = new THREE.Color(0x808080);
camera = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
0.1,
10
);
camera.position.set(0, 1.6, 3);
controls = new OrbitControls(camera, container);
controls.target.set(0, 1.6, 0);
controls.update();
const floorGeometry = new THREE.PlaneGeometry(6, 6);
const floorMaterial = new THREE.ShadowMaterial({
opacity: 0.25,
blending: THREE.CustomBlending,
transparent: false,
});
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
scene.add(new THREE.HemisphereLight(0xbcbcbc, 0xa5a5a5, 3));
const light = new THREE.DirectionalLight(0xffffff, 3);
light.position.set(0, 6, 0);
light.castShadow = true;
light.shadow.camera.top = 3;
light.shadow.camera.bottom = -3;
light.shadow.camera.right = 3;
light.shadow.camera.left = -3;
light.shadow.mapSize.set(4096, 4096);
scene.add(light);
group = new THREE.Group();
scene.add(group);
const loader = new GLTFLoader();
loader.load(
"/models/model.glb",
function (gltf) {
const object = gltf.scene;
object.position.set(0, 0.5, -2);
object.scale.set(0.030, 0.030, 0.030);
object.rotateY(3.1);
group.add(object);
},
undefined,
function (error) {
console.error(error);
}
);
// for ( let i = 0; i < 1; i ++ ) {
// const geometry = geometries[ Math.floor( Math.random() *1) ];
// const material = new THREE.MeshStandardMaterial( {
// color: Math.random() * 0xffffff,
// roughness: 0.7,
// metalness: 0.0
// } );
// const object = new THREE.Mesh( geometry, material );
// object.position.x = Math.random() * 4 - 2;
// object.position.y = Math.random() * 2;
// object.position.z = Math.random() * 4 - 2;
// object.rotation.x = Math.random() * 2 * Math.PI;
// object.rotation.y = Math.random() * 2 * Math.PI;
// object.rotation.z = Math.random() * 2 * Math.PI;
// object.scale.setScalar( Math.random() + 0.5 );
// object.castShadow = true;
// object.receiveShadow = true;
// group.add( object );
// }
//
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animate);
renderer.shadowMap.enabled = true;
renderer.xr.enabled = true;
container.appendChild(renderer.domElement);
document.body.appendChild(
XRButton.createButton(renderer, {
optionalFeatures: ["depth-sensing"],
depthSensing: {
usagePreference: ["gpu-optimized"],
dataFormatPreference: [],
},
})
);
// controllers
controller1 = renderer.xr.getController(0);
controller1.addEventListener("selectstart", onSelectStart);
controller1.addEventListener("selectend", onSelectEnd);
scene.add(controller1);
controller2 = renderer.xr.getController(1);
controller2.addEventListener("selectstart", onSelectStart);
controller2.addEventListener("selectend", onSelectEnd);
scene.add(controller2);
const controllerModelFactory = new XRControllerModelFactory();
controllerGrip1 = renderer.xr.getControllerGrip(0);
controllerGrip1.add(
controllerModelFactory.createControllerModel(controllerGrip1)
);
scene.add(controllerGrip1);
controllerGrip2 = renderer.xr.getControllerGrip(1);
controllerGrip2.add(
controllerModelFactory.createControllerModel(controllerGrip2)
);
scene.add(controllerGrip2);
//
const geometry = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(0, 0, -1),
]);
const line = new THREE.Line(geometry);
line.name = "line";
line.scale.z = 5;
controller1.add(line.clone());
controller2.add(line.clone());
raycaster = new THREE.Raycaster();
//
window.addEventListener("resize", onWindowResize);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onSelectStart(event) {
const controller = event.target;
const intersections = getIntersections(controller);
if (intersections.length > 0) {
const intersection = intersections[0];
const object = intersection.object;
controller.attach(object);
controller.userData.selected = object;
}
alert(JSON.stringify(intersections))
controller.userData.targetRayMode = event.data.targetRayMode;
}
function onSelectEnd(event) {
const controller = event.target;
if (controller.userData.selected !== undefined) {
const object = controller.userData.selected;
object.material.emissive.b = 0;
group.attach(object);
controller.userData.selected = undefined;
}
}
function getIntersections(controller) {
controller.updateMatrixWorld();
raycaster.setFromXRController(controller);
return raycaster.intersectObjects(group.children, false);
}
console.log(intersected)
function intersectObjects(controller) {
// Do not highlight in mobile-ar
if (controller.userData.targetRayMode === "screen") return;
// Do not highlight when already selected
if (controller.userData.selected !== undefined) return;
const intersections = getIntersections(controller);
if (intersections.length > 0) {
const intersection = intersections[0];
alert(JSON.stringify(intersection))
const object = intersection.object;
intersected.push(object);
}
}
function cleanIntersected() {
while (intersected.length) {
intersected.pop();
}
}
//
function animate() {
cleanIntersected();
intersectObjects(controller1);
intersectObjects(controller2);
renderer.render(scene, camera);
}