akbartus / A-Frame-Component-Web-VPS

This is an experimental A-Frame component for web based VPS powered by Immersal's REST API.
MIT License
6 stars 1 forks source link

Example do not work #1

Open micfio opened 2 months ago

micfio commented 2 months ago

Hello i try the example but dont work . Can provide me working sample ? Thanks in advances i found this endpoint url : https://immersal.gitbook.io/sdk/api-documentation/rest-api

akbartus commented 2 months ago

@micfio thank you for your inquiry. I cannot provide working sample as I cannot share info from my developer's page in Immersal. You need to follow the following instructions provided:

  1. Create a free account on Immersal's developer page.
  2. Create a map using Immersal Mapper App (available on AppStore and PlayStore; it is free).
  3. Inside of Immersal's developer page take individual token and map id.
  4. Then use this component.
micfio commented 2 months ago

pheraps you can help me with this? the point cloud ar not visualized. https://zenn.dev/tkada/articles/1b144d1a427148

akbartus commented 2 months ago

Just to be sure: did you add your token and mapID? a-entity webvps="pointCloudSize: 0.05; modelURL: example.glb; token: yourtoken; mapID: 99999; mapType: 0" visible="false"

micfio commented 2 months ago

yes

Inviato da Posta per Windows

Da: Akbar S. Inviato: venerdì 16 agosto 2024 19:50 A: akbartus/A-Frame-Component-Web-VPS Cc: micfio; Mention Oggetto: Re: [akbartus/A-Frame-Component-Web-VPS] Example do not work (Issue#1)

Just to be sure: did you add your token and mapID? a-entity webvps="pointCloudSize: 0.05; modelURL: example.glb; token: yourtoken; mapID: 99999; mapType: 0" visible="false" — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>

micfio commented 2 months ago

yes, not are visualizad tap to place help and seems that tap not work . i have not found addevent listenerr click in the file.

akbartus commented 2 months ago

One more question: does your device support ARCore(Android)? Because without support it will not work. (see compatible devices: https://developers.google.com/ar/devices )

micfio commented 2 months ago

Yes

micfio commented 2 months ago

A forward step : setting token inside script and not passed now the pointcloud map are visualized but remain the tap not work and also the localize.

micfio commented 2 months ago

changed : `// Virtual Positioning System for A-Frame AFRAME.registerComponent("webvps", { schema: { modelURL: { type: "string" }, scale: { type: "vec3", default: { x: 0.5, y: 0.5, z: 0.5 } }, position: { type: "vec3", default: { x: 0, y: 0, z: 0 } }, rotation: { type: "vec3", default: { x: 0, y: 0, z: 0 } }, token: { type: "string" }, mapID: { type: "string" }, mapType: { type: "int", default: 0, oneOf: [0, 1] }, pointCloudSize: { type: "number" } // Changed from int to number }, init: function () { var data = this.data; var el = this.el; let token = data.token; let myMapID = data.mapID; let myMapType = data.mapType; let myPointCloudSize = data.pointCloudSize;

console.log('Component data:', data); // Debugging line

new THREE.GLTFLoader().load(
  data.modelURL,
  function (gltf) {
    var mesh = gltf.scene;
    mesh.scale.set(data.scale.x, data.scale.y, data.scale.z);
    mesh.position.set(data.position.x, data.position.y, data.position.z);
    mesh.rotation.set(
      THREE.MathUtils.degToRad(data.rotation.x),
      THREE.MathUtils.degToRad(data.rotation.y),
      THREE.MathUtils.degToRad(data.rotation.z)
    );
    el.setObject3D("mesh", mesh);
  },
  undefined, 
  function (error) {
    console.error('An error occurred while loading the model:', error);
  }
);

init(token, myMapID, myMapType, myPointCloudSize);
animate();

}, });

// END `

according to: <a-entity webvps="**pointCloudSize: 0.05**; modelURL: tree.glb; token: abcdefjhi; mapID: 99999; mapType: 0" visible="false"></a-entity>

now the parameters are passed and i noth define the token in script. But always the tap and localize not work.

micfio commented 2 months ago

Working code:

`// Load PointCloud loader and ARButton for webXR (three.js) import { PLYLoader } from "https://cdn.skypack.dev/pin/three@v0.131.3-QQa34rwf1xM5cawaQLl8/mode=imports,min/unoptimized/examples/jsm/loaders/PLYLoader.js"; import { ARButton } from "https://cdn.skypack.dev/pin/three@v0.131.3-QQa34rwf1xM5cawaQLl8/mode=imports,min/unoptimized/examples/jsm/webxr/ARButton.js";

// Define all variables let container; let overlay; let locInfo; let camera, scene, renderer; let xrController; let pointCloud; let token; let mapId; let videoWidth = null; let videoHeight = null; let pixelBuffer = null; let cameraIntrinsics = null; let isLocalizing = false; let encodedImage = null; let trackerSpace = null; let gl = null; let readbackFramebuffer = null; let readbackPixels = null; let worker = null; let locAttempt = 0; let locSuccess = 0;

const poses = [];

const LARGE_MAP_THRESHOLD = 50.0; // Define required shaders const vertexShader = uniform float size; attribute vec3 pointColor; varying vec3 vColor; void main() { vColor = pointColor; vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_PointSize = size * ( 300.0 / -mvPosition.z ); gl_Position = projectionMatrix * mvPosition; }; const fragmentShader = uniform vec3 color; uniform sampler2D pointTexture; uniform float alphaTest; varying vec3 vColor; void main() { gl_FragColor = vec4(color * vColor, 1.0); gl_FragColor = gl_FragColor * texture2D(pointTexture, gl_PointCoord); if (gl_FragColor.a < alphaTest) discard; }; const pointCloudMaterial = new THREE.ShaderMaterial({ uniforms: { size: { value: 0.4 }, color: { value: new THREE.Color(0xffffff) }, pointTexture: { value: new THREE.TextureLoader().load("https://cdn.glitch.global/921711e4-380b-4dae-9617-77c8907c1e7d/circle.png") }, alphaTest: { value: 0.1 }, }, vertexShader, fragmentShader, });

// Virtual Positioning System for A-Frame AFRAME.registerComponent("webvps", { schema: { modelURL: { type: "string" }, scale: { type: "vec3", default: { x: 0.5, y: 0.5, z: 0.5 } }, position: { type: "vec3", default: { x: 0, y: 0, z: 0 } }, rotation: { type: "vec3", default: { x: 0, y: 0, z: 0 } }, token: { type: "string" }, mapID: { type: "string" }, mapType: { type: "int", default: 0, oneOf: [0, 1] }, pointCloudSize: { type: "number" } // Changed from int to number }, init: function () { var data = this.data; var el = this.el; let token = data.token; let myMapID = data.mapID; let myMapType = data.mapType; let myPointCloudSize = data.pointCloudSize;

console.log('Component data:', data); // Debugging line

new THREE.GLTFLoader().load(
  data.modelURL,
  function (gltf) {
    var mesh = gltf.scene;
    mesh.scale.set(data.scale.x, data.scale.y, data.scale.z);
    mesh.position.set(data.position.x, data.position.y, data.position.z);
    mesh.rotation.set(
      THREE.MathUtils.degToRad(data.rotation.x),
      THREE.MathUtils.degToRad(data.rotation.y),
      THREE.MathUtils.degToRad(data.rotation.z)
    );
    el.setObject3D("mesh", mesh);
  },
  undefined, 
  function (error) {
    console.error('An error occurred while loading the model:', error);
  }
);

init(token, myMapID, myMapType, myPointCloudSize);
animate();

}, });

// END let myModel = document.querySelector('#mymodel'); // refer to a-frame entity

function init(tokenData, myMapID, myMapType, myPointCloudSize) { mapId = -1; let type = mapTypes.DENSE; let privacy = privacyTypes.PRIVATE; let s = 0&${myMapID}&${myMapType}; console.log(s);

if (s.length > 0) { s.split("&").forEach((item, index) => { console.log(item); switch (index) { case 0: privacy = parseInt(item); break; case 1: mapId = parseInt(item); break; case 2: type = parseInt(item); break; } }); }

if (privacy == privacyTypes.PRIVATE) { token = tokenData; }

worker = new Worker("js/pngworker.js"); worker.onmessage = function(e) { encodedImage = e.data; localize(); }

container = document.getElementById("canvas-parent"); overlay = document.getElementById("overlay"); locInfo = document.getElementById("locinfo");

camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.2, 10000 ); camera.position.set(0.8, 0.6, -2.7); scene = document.querySelector("a-scene").object3D; // change this

// renderer renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); renderer.xr.enabled = true; renderer.xr.addEventListener("sessionstart", function (event) { if (pointCloud) { pointCloud.material.size = 3; } }); renderer.xr.addEventListener("sessionend", function (event) { if (pointCloud) { pointCloud.material.size = 1; } }); container.appendChild(renderer.domElement);

document.body.appendChild( ARButton.createButton(renderer, { requiredFeatures: ["camera-access", "dom-overlay"], domOverlay: { root: overlay }, }) );

gl = renderer.getContext(); readbackFramebuffer = gl.createFramebuffer();

function onSelect() { const session = renderer.xr.getSession && renderer.xr.getSession();

if (session) {
  let referenceSpace = renderer.xr.getReferenceSpace();
  let glLayer = session.renderState.baseLayer;

  session.requestAnimationFrame((time, xrFrame) => {
    let viewerPose = xrFrame.getViewerPose(referenceSpace);

    if (viewerPose) {
      for (const view of viewerPose.views) {
        if (view.camera) {
          let xrCamera = view.camera;
          let binding = new XRWebGLBinding(xrFrame.session, gl);
          let cameraTexture = binding.getCameraImage(xrCamera);
          let viewport =
            xrFrame.session.renderState.baseLayer.getViewport(view);

          videoWidth = xrCamera.width;
          videoHeight = xrCamera.height;

          let bytes = videoWidth * videoHeight * 4;

          if (bytes > 0) {
            if (!readbackPixels || readbackPixels.length != bytes) {
              readbackPixels = new Uint8Array(bytes);
            }

            readbackPixels.fill(0);

            gl.bindTexture(gl.TEXTURE_2D, cameraTexture);
            gl.bindFramebuffer(gl.FRAMEBUFFER, readbackFramebuffer);
            gl.framebufferTexture2D(
              gl.FRAMEBUFFER,
              gl.COLOR_ATTACHMENT0,
              gl.TEXTURE_2D,
              cameraTexture,
              0
            );

            if (
              gl.checkFramebufferStatus(gl.FRAMEBUFFER) ==
              gl.FRAMEBUFFER_COMPLETE
            ) {
              gl.readPixels(
                0,
                0,
                videoWidth,
                videoHeight,
                gl.RGBA,
                gl.UNSIGNED_BYTE,
                readbackPixels
              );
              const e = gl.getError();
              if (e != 0) {
                console.warn("Got a GL error:", e);
              } else {
                let halfHeight = (videoHeight / 2) | 0;
                let bytesPerRow = videoWidth * 4;

                let temp = new Uint8Array(bytesPerRow);
                for (let y = 0; y < halfHeight; ++y) {
                  let topOffset = y * bytesPerRow;
                  let bottomOffset = (videoHeight - y - 1) * bytesPerRow;

                  temp.set(
                    readbackPixels.subarray(
                      topOffset,
                      topOffset + bytesPerRow
                    )
                  );
                  readbackPixels.copyWithin(
                    topOffset,
                    bottomOffset,
                    bottomOffset + bytesPerRow
                  );
                  readbackPixels.set(temp, bottomOffset);
                }

                if (!pixelBuffer || pixelBuffer.length != bytes / 4) {
                  pixelBuffer = new Uint8Array(bytes / 4);
                }

                pixelBuffer.fill(0);

                let grayIndex = 0;
                for (let i = 0; i < bytes; i += 4) {
                  pixelBuffer[grayIndex++] = readbackPixels[i + 1];
                }

                const cameraViewport = {
                  width: videoWidth,
                  height: videoHeight,
                  x: 0,
                  y: 0,
                };

                cameraIntrinsics = getCameraIntrinsics(
                  view.projectionMatrix,
                  cameraViewport
                );

                trackerSpace = new THREE.Matrix4();
                trackerSpace.copy(camera.matrixWorld);

                worker.postMessage([pixelBuffer, videoWidth, videoHeight]);
              }
            } else {
              console.warn("Framebuffer incomplete!");
            }

            gl.bindFramebuffer(
              gl.FRAMEBUFFER,
              xrFrame.session.renderState.baseLayer.framebuffer
            );
          }
        }
      }
    }
  });
}

}

xrController = renderer.xr.getController(0); xrController.addEventListener("select", onSelect); scene.add(xrController);

// resize

window.addEventListener("resize", onWindowResize, false);

if (mapId != -1) { if (type == mapTypes.SPARSE) { renderer.outputEncoding = THREE.LinearEncoding; renderer.toneMapping = THREE.NoToneMapping; loadSparse(mapId, token, myPointCloudSize); } if (type == mapTypes.DENSE) { renderer.outputEncoding = THREE.LinearEncoding; renderer.toneMapping = THREE.NoToneMapping; loadDense(mapId, token, myPointCloudSize); } loadPoses(mapId, token); } }

function localize() { if (isLocalizing) return; isLocalizing = true; locAttempt++; const json = { token: token, fx: cameraIntrinsics.x, fy: cameraIntrinsics.y, ox: cameraIntrinsics.z, oy: cameraIntrinsics.w, param1: 0, param2: 12, param3: 0.0, param4: 2.0, mapIds: [{ id: mapId }], };

const payload = new Blob([JSON.stringify(json), "\0", encodedImage]); fetch(BASE_URL + "localize", { method: "POST", body: payload, headers: { "Content-Type": "application/json", }, }) .then((response) => { if (response.ok) {

    return response.json();
  } else {
    console.warn("error:" + JSON.stringify(response.json()));
    isLocalizing = false;
  }
})
.then((data) => {
  if (data.success==true) {

    console.log(data);
    console.log("Relocalized successfully");
    locSuccess++;

    if (pointCloud) {
      let position = new THREE.Vector3();
      let rotation = new THREE.Quaternion();
      let scale = new THREE.Vector3();

      const cloudSpace = new THREE.Matrix4();
      cloudSpace.set(
        data.r00,
        -data.r01,
        -data.r02,
        data.px,
        data.r10,
        -data.r11,
        -data.r12,
        data.py,
        data.r20,
        -data.r21,
        -data.r22,
        data.pz,
        0,
        0,
        0,
        1
      );

      const m = new THREE.Matrix4().multiplyMatrices(
        trackerSpace,
        cloudSpace.invert()
      );

      m.decompose(position, rotation, scale);
      // Set cube to position

      pointCloud.position.set(position.x, position.y, position.z);
      pointCloud.quaternion.set(
        rotation.x,
        rotation.y,
        rotation.z,
        rotation.w
      );
      pointCloud.scale.set(scale.x, scale.y, scale.z);

      // New position of myModel based on localization 
        if (locSuccess <= 10) {

        myModel.setAttribute("visible", true);
        myModel.setAttribute(
          "position",
          `${position.x} ${position.y} ${position.z - 2}`
        );
        myModel.setAttribute(
          "rotation",
          `${THREE.MathUtils.degToRad(
            rotation.x
          )}, ${THREE.MathUtils.degToRad(
            rotation.y
          )}, ${THREE.MathUtils.degToRad(rotation.z)}`
        );
        myModel.setAttribute("scale", `${scale.x}, ${scale.y}, ${scale.z}`);
      }
    }
  } else {
    myModel.setAttribute("visible", false);
    console.log("Failed to relocalize");
  }
  locInfo.innerText =
    "Successful localizations: " + locSuccess + "/" + locAttempt;
  isLocalizing = false;
})
.catch((error) => console.warn("error:" + error));

}

function loadPoses(mapId, token) { var json = { token: token, id: mapId };

fetch(BASE_URL + DOWNLOAD_POSES, { method: "POST", body: JSON.stringify(json), headers: { "Content-Type": "application/json", }, }) .then((response) => { if (response.ok) { return response.json(); } else { console.log("error:" + JSON.stringify(response.json().error)); } }) .then((data) => { const n = data.poses.length; for (let i = 0; i < n; i++) { const ax = new THREE.AxesHelper(0.5); const d = data.poses[i];

    let position = new THREE.Vector3();
    let rotation = new THREE.Quaternion();
    let scale = new THREE.Vector3();

    const m = new THREE.Matrix4();
    m.set(
      d.r00,
      d.r01,
      d.r02,
      d.px,
      d.r10,
      d.r11,
      d.r12,
      d.py,
      d.r20,
      d.r21,
      d.r22,
      d.pz,
      0,
      0,
      0,
      1
    );

    m.decompose(position, rotation, scale);
    ax.position.set(position.x, position.y, position.z);
    ax.quaternion.set(rotation.x, rotation.y, rotation.z, rotation.w);
    ax.scale.set(scale.x, scale.y, scale.z);

    poses.push(ax); // check this
    // Add here something
  }
})
.catch((error) => console.log("error:" + error));

}

function loadSparse(mapId, token, myPointCloudSize) { let loader = new PLYLoader(); let url = BASE_URL + DOWNLOAD_SPARSE; if (token != null) { url += "?token=" + token + "&id=" + mapId; } else { url += "?id=" + mapId; } loader.load(url, function (geometry) { const pc = new THREE.BufferGeometry(); const pointCount = geometry.getAttribute("position").count; pc.setAttribute("position", geometry.getAttribute("position")); pc.setAttribute("pointColor", geometry.getAttribute("color"));

pointCloud = new THREE.Points(pc, pointCloudMaterial);

scene.add(pointCloud);
console.log(pointCloud);

const bbox = new THREE.Box3().setFromObject(pointCloud);
let size = bbox.getSize(new THREE.Vector3()).length();
let center = bbox.getCenter(new THREE.Vector3());

pointCloudMaterial.uniforms.size.value = myPointCloudSize;

if (size > LARGE_MAP_THRESHOLD) {
  size = LARGE_MAP_THRESHOLD;
  center = new THREE.Vector3();

  pointCloudMaterial.uniforms.size.value = myPointCloudSize;
}
camera.position.copy(center);
camera.position.x += size / 2.0;
camera.position.y += size / 5.0;
camera.position.z += size / 2.0;
camera.position.z = -camera.position.z;
camera.lookAt(center);

}); }

function loadDense(mapId, token, myPointCloudSize) { let loader = new PLYLoader(); let url = BASE_URL + DOWNLOAD_DENSE; if (token != null) { url += "?token=" + token + "&id=" + mapId; } else { url += "?id=" + mapId; }

loader.load(url, function (geometry) { let material, mesh; geometry.computeVertexNormals(); geometry.computeFaceNormals(); material = new THREE.PointsMaterial({ color: 0x80ff00, vertexColors: THREE.VertexColors, size: 3, sizeAttenuation: false, }); mesh = new THREE.Mesh(geometry, material); // Put smth here console.log("Load");

const bbox = new THREE.Box3().setFromObject(mesh);
let size = bbox.getSize(new THREE.Vector3()).length();
let center = bbox.getCenter(new THREE.Vector3());
pointCloudMaterial.uniforms.size.value = myPointCloudSize;

if (size > LARGE_MAP_THRESHOLD) {
  size = LARGE_MAP_THRESHOLD;
  center = new THREE.Vector3();

  pointCloudMaterial.uniforms.size.value = myPointCloudSize;
}
camera.position.copy(center);
camera.position.x += size / 2.0;
camera.position.y += size / 5.0;
camera.position.z += size / 2.0;

camera.position.z = -camera.position.z;
camera.lookAt(center);

mesh.castShadow = true;
mesh.receiveShadow = true;

pointCloud = mesh;
scene.add(pointCloud);

}); }

function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }

// Get camera intrinsics function getCameraIntrinsics(projectionMatrix, viewport) { const p = projectionMatrix; let u0 = ((1 - p[8]) viewport.width) / 2 + viewport.x; let v0 = ((1 - p[9]) viewport.height) / 2 + viewport.y; let ax = (viewport.width / 2) p[0]; let ay = (viewport.height / 2) p[5];

const intr = { x: ax, y: ay, z: u0, w: v0 }; return intr; }

function animate() { renderer.setAnimationLoop(render); }

function render() { renderer.render(scene, camera); } `

pmgworker.js code: `importScripts('UPNG.js', 'pako.min.js');

onmessage = function(msg) { const pixels = msg.data[0]; const width = msg.data[1]; const height = msg.data[2]; const png = UPNG.encodeLL([pixels], width, height, 1, 0, 8, 0);

postMessage(png); }`

Need UPNG.js and pako.min.js in js folder to work properly.

micfio commented 2 months ago

aframe-web-vps.zip