{"_id":"@ar-js-org/arjs-plugin-threejs","name":"@ar-js-org/arjs-plugin-threejs","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.1":{"name":"@ar-js-org/arjs-plugin-threejs","version":"0.1.1","description":"Three.js renderer plugin for AR.js-core","type":"module","main":"dist/arjs-plugin-threejs.js","module":"dist/arjs-plugin-threejs.mjs","types":"types/index.d.ts","exports":{".":{"import":"./dist/arjs-plugin-threejs.mjs","require":"./dist/arjs-plugin-threejs.js","default":"./dist/arjs-plugin-threejs.mjs"}},"sideEffects":false,"scripts":{"build":"npm run build:vite && npm run build:types","build:types":"tsc","build:vite":"vite build","dev:vite":"vite","format":"prettier --write .","format:check":"prettier --check .","lint":"eslint . --ext .js","prepare":"husky","prepublishOnly":"npm run build","serve:vite":"vite preview","test":"vitest run --coverage","test:watch":"vitest"},"keywords":["ar.js","threejs","augmented-reality","webar","webxr"],"author":{"name":"Walter Perdan @kalwalt https://github.com/kalwalt"},"license":"MIT","devDependencies":{"@vitest/coverage-v8":"^4.0.16","eslint":"^9.39.2","husky":"^9.1.7","jsdom":"^27.3.0","prettier":"^3.7.4","three":"^0.182.0","typescript":"^5.9.3","vite":"^7.3.0","vitest":"^4.0.16"},"repository":{"type":"git","url":"git+https://github.com/AR-js-org/arjs-plugin-threejs.git"},"_id":"@ar-js-org/arjs-plugin-threejs@0.1.1","gitHead":"c05cd5533ab03ed9e7a15b5d0f65dba2a8375f73","bugs":{"url":"https://github.com/AR-js-org/arjs-plugin-threejs/issues"},"homepage":"https://github.com/AR-js-org/arjs-plugin-threejs#readme","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-r+B7LTYaRLiF0iQfdqIqPQFiDQR7sP+Fbkz91jqW0DzlSqHxsC/AfcgYDDr4uCFvi6ArFCHsGePKuFIwzxpCDw==","shasum":"13ca2268de595dc9f7ebfd5ae05c420ac3e29776","tarball":"https://registry.npmjs.org/@ar-js-org/arjs-plugin-threejs/-/arjs-plugin-threejs-0.1.1.tgz","fileCount":12,"unpackedSize":38134,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBBMMBg/cZCMl9+Mr13frfYmAEP9uw+sjf2WiIZv3RuPAiAaSXoDKH66SRdNqqDvqitQOvsa3wB/bO5mvpA2V2irAQ=="}]},"_npmUser":{"name":"kalwalt","email":"info@kalwaltart.it"},"directories":{},"maintainers":[{"name":"kalwalt","email":"info@kalwaltart.it"},{"name":"nickw1","email":"nickw4426@gmail.com"},{"name":"nicolocarpignoli","email":"nicolocarpignoli@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/arjs-plugin-threejs_0.1.1_1766514390321_0.5786776853321318"},"_hasShrinkwrap":false}},"time":{"created":"2025-12-23T18:26:30.176Z","0.1.1":"2025-12-23T18:26:30.474Z","modified":"2025-12-23T18:26:30.871Z"},"maintainers":[{"name":"kalwalt","email":"info@kalwaltart.it"},{"name":"nickw1","email":"nickw4426@gmail.com"},{"name":"nicolocarpignoli","email":"nicolocarpignoli@gmail.com"}],"description":"Three.js renderer plugin for AR.js-core","homepage":"https://github.com/AR-js-org/arjs-plugin-threejs#readme","keywords":["ar.js","threejs","augmented-reality","webar","webxr"],"repository":{"type":"git","url":"git+https://github.com/AR-js-org/arjs-plugin-threejs.git"},"author":{"name":"Walter Perdan @kalwalt https://github.com/kalwalt"},"bugs":{"url":"https://github.com/AR-js-org/arjs-plugin-threejs/issues"},"license":"MIT","readme":"# arjs-plugin-threejs ✨🧩\n\n<p style=\"text-align: center;\">\n  <a href=\"https://github.com/AR-js-org/arjs-plugin-threejs/stargazers\">\n    <img src=\"https://img.shields.io/github/stars/AR-js-org/arjs-plugin-threejs?style=flat-square\" alt=\"GitHub Stars\">\n  </a>\n  <a href=\"https://github.com/AR-js-org/arjs-plugin-threejs/network/members\">\n    <img src=\"https://img.shields.io/github/forks/AR-js-org/arjs-plugin-threejs?style=flat-square\" alt=\"GitHub Forks\">\n  </a>\n  <a href=\"https://github.com/AR-js-org/arjs-plugin-threejs/actions/workflows/CI.yml\">\n    <img src=\"https://github.com/AR-js-org/arjs-plugin-threejs/actions/workflows/CI.yml/badge.svg\" alt=\"CI Status\">\n  </a>\n  <a href=\"https://github.com/AR-js-org/arjs-plugin-threejs/blob/main/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/AR-js-org/arjs-plugin-threejs?style=flat-square\" alt=\"License\">\n  </a>\n  <a href=\"https://github.com/AR-js-org/arjs-plugin-threejs/issues\">\n    <img src=\"https://img.shields.io/github/issues/AR-js-org/arjs-plugin-threejs?style=flat-square\" alt=\"Open Issues\">\n  </a>\n  <a href=\"https://img.shields.io/badge/three.js-0.161.0-000000?style=flat-square\">\n    <img src=\"https://img.shields.io/badge/three.js-0.161.0-000000?style=flat-square\" alt=\"Three.js Version\">\n  </a>\n</p>\n\n> 🧪 A Three.js renderer plugin for **AR.js-core**: mounts a WebGL canvas, consumes AR marker + camera events, and exposes per‑marker Three.js `Group` anchors for you to attach content.  \n> 🔧 Defaults replicate classic AR.js axis handling.  \n> 🚀 Designed for extensibility, testability (renderer injection), and modern ESM builds.\n\n---\n\n## Table of Contents 📚\n\n- [Features](#features-)\n- [Install / Build](#install--build-)\n- [Quick Start](#quick-start-engine--artoolkit--threejs-plugin-)\n- [Events](#events-handled-)\n- [Options](#options-)\n- [Camera Projection](#camera-projection-)\n- [Anchors & Adding Content](#anchors-and-how-to-add-content-)\n- [Testing](#testing-)\n- [CI](#ci-)\n- [Compatibility](#compatibility-)\n- [Roadmap Ideas](#roadmap-ideas-)\n- [License](#license-)\n\n## Features 🌟\n\n- ✅ Unified handling for `ar:marker`, raw `ar:getMarker`, legacy `ar:markerFound / Updated / Lost`\n- 🔄 Automatic AR.js classic axis transform chain (`R_y(π) * R_z(π) * modelViewMatrix * R_x(π/2)`)\n- 🧬 Optional experimental path (`invertModelView`, `applyAxisFix`)\n- 🪝 Lazy anchor creation (create Three.js `Group` only when a marker first appears)\n- 🎛 Debug helpers: scene & per‑anchor `AxesHelper`\n- 🧪 Test-friendly: inject your own renderer via `rendererFactory`\n- 🏃 Dual render triggers: `engine:update` or `requestAnimationFrame` fallback\n- 🛡 Confidence filtering on marker events\n- 🧹 Clean disable/dispose lifecycle\n\n## Install / Build 🛠\n\n> Note: the `dist` and `types` folders are not committed. If you modify the source, run `npm install`, then rebuild with `npm run build:vite` and `npm run build:types` before using the package or publishing.\n\n```bash\nnpm run build:vite\n```\n\nOutputs:\n\n- ESM: `dist/arjs-plugin-threejs.mjs`\n- CJS: `dist/arjs-plugin-threejs.js`\n- Source maps included\n\nServe the example (choose one):\n\n```bash\n# If example has its own dev scripts\ncd examples/minimal\nnpm i\nnpm run dev\n\n# OR from repo root (so relative dist path works)\nnpx http-server .\n# Open: http://localhost:8080/examples/minimal/\n```\n\n## Quick start (Engine + Artoolkit + Three.js plugin) 🚀\n\n```js\nimport { Engine, webcamPlugin, defaultProfilePlugin } from \"ar.js-core\";\nimport { ThreeJSRendererPlugin } from \"@AR-js-org/arjs-plugin-threejs\";\n\n// 1) Engine & core plugins\nconst engine = new Engine();\nengine.pluginManager.register(defaultProfilePlugin.id, defaultProfilePlugin);\nengine.pluginManager.register(webcamPlugin.id, webcamPlugin);\nconst ctx = engine.getContext();\nawait engine.pluginManager.enable(defaultProfilePlugin.id, ctx);\nawait engine.pluginManager.enable(webcamPlugin.id, ctx);\n\n// 2) Artoolkit plugin\nconst { ArtoolkitPlugin } =\n  await import(\"./vendor/arjs-plugin-artoolkit/arjs-plugin-artoolkit.esm.js\");\nconst artoolkit = new ArtoolkitPlugin({\n  cameraParametersUrl: \"/path/to/camera_para.dat\",\n  minConfidence: 0.6,\n});\nawait artoolkit.init(ctx);\nawait artoolkit.enable();\n\n// 3) Projection\nconst proj = artoolkit.getProjectionMatrix?.();\nconst arr = proj?.toArray ? proj.toArray() : proj;\nif (Array.isArray(arr) && arr.length === 16) {\n  engine.eventBus.emit(\"ar:camera\", { projectionMatrix: arr });\n}\n\n// 4) Three.js plugin\nconst threePlugin = new ThreeJSRendererPlugin({\n  container: document.getElementById(\"viewport\"),\n  useLegacyAxisChain: true,\n  changeMatrixMode: \"modelViewMatrix\",\n  preferRAF: true,\n  // debugSceneAxes: true,\n  // debugAnchorAxes: true,\n});\nawait threePlugin.init(engine);\nawait threePlugin.enable();\n\n// 5) Start engine loop\nengine.start();\n```\n\n## Events handled 🔔\n\n| Event                                               | Payload                     | Purpose                                               |\n| --------------------------------------------------- | --------------------------- | ----------------------------------------------------- |\n| `ar:marker`                                         | `{ id, matrix?, visible? }` | Unified high-level marker pose/visibility             |\n| `ar:getMarker`                                      | `{ matrix, marker: {...} }` | Raw worker-level pose (plugin extracts ID/confidence) |\n| `ar:markerFound / ar:markerUpdated / ar:markerLost` | legacy shapes               | Adapted internally to `ar:marker`                     |\n| `ar:camera`                                         | `{ projectionMatrix }`      | Sets camera projection                                |\n| `engine:update`                                     | `any`                       | Optional frame trigger (in addition to RAF)           |\n\n## Options ⚙️\n\n| Option               | Type                 | Default           | Description                                |\n| -------------------- | -------------------- | ----------------- | ------------------------------------------ |\n| `container`          | `HTMLElement`        | `document.body`   | Mount target for canvas                    |\n| `preferRAF`          | `boolean`            | `true`            | Render each RAF even w/o `engine:update`   |\n| `minConfidence`      | `number`             | `0`               | Ignore `ar:getMarker` below confidence     |\n| `useLegacyAxisChain` | `boolean`            | `true`            | Use classic AR.js transform chain          |\n| `changeMatrixMode`   | `string`             | `modelViewMatrix` | Or `cameraTransformMatrix` (inverts)       |\n| `invertModelView`    | `boolean`            | `false`           | Experimental (disabled if legacy chain on) |\n| `applyAxisFix`       | `boolean`            | `false`           | Experimental axis correction (Y/Z π)       |\n| `debugSceneAxes`     | `boolean`            | `false`           | Show `AxesHelper` at scene origin          |\n| `sceneAxesSize`      | `number`             | `2`               | Size for scene axes helper                 |\n| `debugAnchorAxes`    | `boolean`            | `false`           | Add `AxesHelper` per anchor                |\n| `anchorAxesSize`     | `number`             | `0.5`             | Size for anchor axes helper                |\n| `rendererFactory`    | `Function` \\| `null` | `null`            | Inject custom renderer (testing)           |\n\nClassic AR.js chain:\n\n```\nfinalMatrix = R_y(π) * R_z(π) * modelViewMatrix * R_x(π/2)\n```\n\nIf `changeMatrixMode === 'cameraTransformMatrix'`, invert at the end.\n\n## Camera Projection 🎯\n\n```js\nconst proj = artoolkit.getProjectionMatrix();\nconst arr = proj?.toArray ? proj.toArray() : proj;\nif (Array.isArray(arr) && arr.length === 16) {\n  engine.eventBus.emit(\"ar:camera\", { projectionMatrix: arr });\n}\n```\n\nLook for log: `Projection applied`.\n\n## Anchors and how to add content 🧱\n\nAnchors are created lazily from the first pose event.\n\n```js\nengine.eventBus.on(\"ar:getMarker\", (d) => {\n  const id = String(\n    d?.marker?.markerId ??\n      d?.marker?.id ??\n      d?.marker?.pattHandle ??\n      d?.marker?.uid ??\n      d?.marker?.index ??\n      \"0\",\n  );\n\n  // Add content once anchor exists\n  setTimeout(() => {\n    const anchor = threePlugin.getAnchor(id);\n    if (anchor && !anchor.userData._content) {\n      anchor.userData._content = true;\n      const cube = new THREE.Mesh(\n        new THREE.BoxGeometry(0.5, 0.5, 0.5),\n        new THREE.MeshBasicMaterial({ color: 0xff00ff }),\n      );\n      cube.position.y = 0.25;\n      anchor.add(cube);\n    }\n  }, 0);\n\n  // Bridge raw to unified\n  if (Array.isArray(d?.matrix) && d.matrix.length === 16) {\n    engine.eventBus.emit(\"ar:marker\", { id, matrix: d.matrix, visible: true });\n  }\n});\n```\n\n## Testing 🧪\n\nRun tests:\n\n```bash\nnpm test\n```\n\nWatch:\n\n```bash\nnpm run test:watch\n```\n\nCoverage includes:\n\n- Axis chain vs. experimental path\n- Inversion & axis fix effects\n- Confidence filtering\n- Anchor lifecycle (create, reuse, visibility)\n- RAF fallback vs engine:update\n- Projection & inverse\n- Disable/Dispose cleanup\n- Debug helpers presence\n- Matrix invariants (`matrixAutoUpdate=false`)\n\nTest renderer injection example:\n\n```js\nconst fakeRenderer = {\n  domElement: document.createElement(\"canvas\"),\n  setPixelRatio() {},\n  setClearColor() {},\n  setSize() {},\n  render() {},\n  dispose() {},\n};\nconst plugin = new ThreeJSRendererPlugin({\n  rendererFactory: () => fakeRenderer,\n});\n```\n\n## CI 🤖\n\nGitHub Actions workflow (`.github/workflows/ci.yml`) runs:\n\n- Install\n- Build\n- Tests (Node version defined in `.nvmrc` file)\n  Badge above shows current status.\n\n## Type Definitions 🔤🧾\n\nTypeScript declaration files are included in the `types` folder. Prefer importing types from the provided declarations:\n\n- `types/index.d.ts`\n- `types/threejs-renderer-plugin.d.ts`\n\nSource maps (`.d.ts.map`) are included for better editor/IDE support.\n\n## Compatibility 🔄\n\n- Built & tested with Three.js 0.161.x\n- Requires AR.js-core engine abstraction with an event bus (`on/off/emit`)\n- Should work with any tracking plugin that can emit marker IDs + 4x4 pose matrices\n\n## Roadmap Ideas 🧭\n\n- 🔌 Additional renderer plugins (Babylon / PlayCanvas)\n- 🧷 Multi-marker composition helpers\n- 🌀 Pose smoothing module (optional add-on)\n- 💡 Example gallery with animated models & GLTF loader integration\n- 🧪 Visual regression tests (screenshot-based) in CI\n\n## License 📄\n\nMIT © AR.js Org\n\n---\n\nMade with ❤️ for Web AR. Contributions welcome! Open an issue / PR 🛠\n","readmeFilename":"README.md","_rev":"1-02ef9eb0d176487ebe5a9a019ce016ff"}