├── .gitmodules ├── .gitignore ├── test ├── env_256_cornell_box.hdr ├── env_256_cornell_box.png ├── meshfile_object_gltf_optimized.bin ├── meshfile_object_gltf_optimized.ktx2 ├── Readme.md ├── empty_scene.html ├── camera_control.html ├── transparent_ellipsoid.html ├── meshfile_geometry_obj.html ├── set_color.html ├── meshfile_geometry_stl.html ├── meshfile_object_obj.html ├── controls.html ├── texture_text.html ├── orthographic_camera.html ├── render_callback.html ├── animation2.html ├── chained_properties.html ├── meshfile_object_gltf_optimized.html ├── webxr.html ├── meshfile_object_dae.html ├── meshfile_object_gltf.html ├── meshfile_geometry_dae.html ├── opacity.html ├── background.html ├── animation.html └── fatlines.html ├── data └── HeadTextureMultisense.png ├── rebuild ├── Dockerfile └── rebuild.sh ├── package.json ├── webpack.config.js ├── dist ├── index.html └── main.min.js.THIRD_PARTY_LICENSES.json ├── LICENSE └── Readme.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/main.js 3 | dist/main.min.js.LICENSE.txt 4 | yarn-error.log 5 | -------------------------------------------------------------------------------- /test/env_256_cornell_box.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshcat-dev/meshcat/master/test/env_256_cornell_box.hdr -------------------------------------------------------------------------------- /test/env_256_cornell_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshcat-dev/meshcat/master/test/env_256_cornell_box.png -------------------------------------------------------------------------------- /data/HeadTextureMultisense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshcat-dev/meshcat/master/data/HeadTextureMultisense.png -------------------------------------------------------------------------------- /test/meshfile_object_gltf_optimized.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshcat-dev/meshcat/master/test/meshfile_object_gltf_optimized.bin -------------------------------------------------------------------------------- /test/meshfile_object_gltf_optimized.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshcat-dev/meshcat/master/test/meshfile_object_gltf_optimized.ktx2 -------------------------------------------------------------------------------- /rebuild/Dockerfile: -------------------------------------------------------------------------------- 1 | # This is used by rebuild.sh to snapshot the node environment. 2 | FROM node:20.11.0 AS build 3 | WORKDIR /usr/src/app 4 | COPY tmp/package.json ./ 5 | COPY tmp/yarn.lock ./ 6 | COPY tmp/webpack.config.js ./ 7 | ENV YARN_CACHE_FOLDER=/root/.yarn 8 | ENV NODE_OPTIONS=--openssl-legacy-provider 9 | RUN \ 10 | --mount=type=cache,target=/root/.yarn \ 11 | --mount=type=cache,target=./node_modules/.cache/webpack \ 12 | yarn install 13 | ENTRYPOINT npm run build 14 | -------------------------------------------------------------------------------- /test/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # Testing MeshCat 3 | 4 | This directory contains various HTML test files that each exercise features of MeshCat. To view the tests, open a local web server: 5 | 6 | python3 -m http.server 7000 7 | 8 | After running that command, open http://127.0.0.1:7000/ and browse through the index of tests. Use Ctrl-C to exit the server. 9 | 10 | Some (but not all) test cases will also work correctly when viewed directly as file:// URLs, but we do not recommend this. 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meshcat", 3 | "version": "0.0.1", 4 | "main": "src/index.js", 5 | "repository": "https://github.com/rdeits/meshcat", 6 | "author": "Robin Deits", 7 | "license": "MIT", 8 | "devDependencies": { 9 | "webpack": "^5.89.0", 10 | "webpack-cli": "^5.1.4", 11 | "webpack-license-plugin": "^4.4.2" 12 | }, 13 | "dependencies": { 14 | "@msgpack/msgpack": "^2.8.0", 15 | "ccapture.js": "aceslowman/ccapture.js#npm-fix", 16 | "dat.gui": "^0.7.9", 17 | "three": "^0.176.0", 18 | "three-wtm": "^1.0", 19 | "wwobjloader2": "^6.2.1", 20 | "yarn": "^1.22.21" 21 | }, 22 | "scripts": { 23 | "build": "webpack", 24 | "watch": "webpack --watch" 25 | }, 26 | "resolutions": { 27 | "three": "^0.176.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const LicensePlugin = require('webpack-license-plugin') 2 | 3 | module.exports = [{ 4 | entry: './src/index.js', 5 | output: { 6 | library: "MeshCat", 7 | libraryTarget: 'umd' 8 | }, 9 | mode: "development", 10 | devtool: "eval-cheap-source-map" 11 | }, { 12 | entry: './src/index.js', 13 | output: { 14 | filename: "main.min.js", 15 | library: "MeshCat", 16 | libraryTarget: 'umd' 17 | }, 18 | mode: "production", 19 | module: { 20 | rules: [ 21 | { 22 | test: /\/libs\/(basis|draco)\//, 23 | type: 'asset/inline' 24 | } 25 | ] 26 | }, 27 | plugins: [ 28 | new LicensePlugin({ 29 | outputFilename: "main.min.js.THIRD_PARTY_LICENSES.json", 30 | licenseOverrides: { 31 | 'wwobjloader2@6.2.1': 'MIT', 32 | } 33 | }) 34 | ], 35 | }]; 36 | -------------------------------------------------------------------------------- /test/empty_scene.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 20 | 21 | 22 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 20 | 21 | 22 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Robin Deits 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rebuild/rebuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Re-generates the files in `meshcat/dist` using a reproducible Docker build. 4 | # 5 | # This script uses Docker. For installation instructions, refer to MeshCat's 6 | # top-level Readme.md file. 7 | 8 | # Fail-fast on errors. 9 | set -euo pipefail 10 | 11 | # Use the buildkit flavor of docker. 12 | export DOCKER_BUILDKIT=1 13 | 14 | # Set our working to directory `meshcat/rebuild/`. 15 | cd "${BASH_SOURCE%/*}" 16 | 17 | # Copy the project files somewhere that docker can find them. 18 | rm -rf tmp 19 | mkdir -p tmp 20 | cp -p ../package.json ./tmp/ 21 | cp -p ../webpack.config.js ./tmp/ 22 | cp -p ../yarn.lock ./tmp/ 23 | 24 | # Tell webpack to exit immediately once it's finished building. 25 | sed -i -e 's#watch: true#watch: false#' ./tmp/webpack.config.js 26 | 27 | # Run `yarn install` and snapshot the result into an image. 28 | image="meshcat-webpack:latest" 29 | docker build -t ${image} . 30 | rm -rf tmp 31 | 32 | # Starting from that image, run webpack against our `src` file(s). 33 | container="meshcat-webpack" 34 | (docker container rm ${container} 2>&1 || true) > /dev/null 35 | docker run \ 36 | --mount type=bind,source="$(cd .. && pwd)"/src,target=/usr/src/app/src \ 37 | --name=${container} ${image} 38 | 39 | # Copy the relevant webpack outputs into `meshcat/dist/. 40 | for name in main.min.js main.min.js.THIRD_PARTY_LICENSES.json; do 41 | docker cp ${container}:/usr/src/app/dist/${name} ../dist/ 42 | done 43 | 44 | # Clean up. 45 | (docker container rm ${container} 2>&1 || true) > /dev/null 46 | 47 | echo "rebuild.sh: SUCCESS!" 48 | -------------------------------------------------------------------------------- /test/camera_control.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 35 | 36 | 37 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /test/transparent_ellipsoid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 30 | 31 | 32 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /test/meshfile_geometry_obj.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 78 | 79 | 80 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /test/set_color.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 84 | 85 | 86 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /test/meshfile_geometry_stl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 39 | 40 | 41 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /test/meshfile_object_obj.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 86 | 87 | 88 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /test/controls.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 62 | 63 | 64 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /test/texture_text.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat - setting opacity 6 | 7 | 8 |
9 | You should see "Hello, world!" standing upright, 10 | approximately filling the whole screen. 11 |
12 |
13 |
14 | 15 | 71 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /test/orthographic_camera.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 97 | 98 | 99 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /test/render_callback.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 | Open the console in developer tools (F12). 10 |

11 | Click the button below to turn on/off camera tracking. 12 |

13 | This webpage uses the render callback mechanism to simply print the 14 | camera's world transform whenever the scene gets rendered. As you 15 | pan the camera (or change the visibility of objects) a new message 16 | gets written to the console including the camera's pose. 17 |

18 | Initially, the callback is a no-op. Click the button below to toggle 19 | the callback behavior. 20 |

21 | Even without moving the camera or changing the scene, simply enabling 22 | a callback should evaluate it; when you click the button, the camera 23 | pose should be printed to the console immediately. 24 |

25 |
[If you read this, the page is broken]
26 |
27 | 28 |
29 | 30 |
31 | 32 | 33 | 89 | 90 | 91 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /test/animation2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | MeshCat 10 | 11 | 12 |
13 |
14 | 15 | 16 | 182 | 183 | 184 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /test/chained_properties.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 | Open the developer console (F12), and monitor *all* messages. All 10 | errors after "TEST START" and before "TEST END" should be predicted 11 | before they happen. Predicted but missing errors and unpredicted 12 | errors should be considered a defect. 13 |
14 |
15 |
16 | 17 | 18 | 175 | 176 | 177 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /test/meshfile_object_gltf_optimized.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 14 | 15 | 16 | 287 | 288 | 289 | 300 | 301 | 302 | -------------------------------------------------------------------------------- /test/webxr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MeshCat WebXR 7 | 8 | 9 |
10 | Click the "Run Next Step" button below. With each click, a new command is 11 | sent to the viewer; it will affect what is presented on the screen and 12 | printed in the console. Compare what is claimed against what you see. 13 | 16 |
17 |
18 | 19 | 20 | 146 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /test/meshfile_object_dae.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 178 | 179 | 180 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /test/meshfile_object_gltf.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 322 | 323 | 324 | 335 | 336 | 337 | -------------------------------------------------------------------------------- /test/meshfile_geometry_dae.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 187 | 188 | 189 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /test/opacity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MeshCat - setting opacity 7 | 8 | 9 | 10 |
11 | There are four tetrahedra and one box. 12 | 25 | 26 | There are two sliders: "Modulated opacity" and "opacity". Changing 27 | their values will change the opacity of the meshes. 28 | 34 | Either slider can be manipulated at any time. The last slider 35 | modified determines the opacity rule currently exhibited. 36 |

37 | Note: when transparent objects lie within transparent objects, 38 | there are view-dependent artifacts (visibility and color changes). 39 | 40 |

41 |
42 | Opacity shown: glTF transparency. 43 |
44 |
45 |
46 |
47 | 48 | 49 | 128 | 129 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /test/background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 | Test how the background interacts with the camera. 10 | 11 | Try the following: 12 | 17 |
18 | 19 |
20 |
21 | 22 | 23 | 238 | 239 | 240 | 261 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /test/animation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 83 | 84 | 85 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /test/fatlines.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MeshCat - Fat Lines (Line2) Test 6 | 7 | 8 |
9 |
10 | 11 | 12 | 385 | 386 | 397 | 398 | 399 | -------------------------------------------------------------------------------- /dist/main.min.js.THIRD_PARTY_LICENSES.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "@msgpack/msgpack", 4 | "version": "2.8.0", 5 | "author": "The MessagePack community", 6 | "repository": "https://github.com/msgpack/msgpack-javascript.git", 7 | "source": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz", 8 | "license": "ISC", 9 | "licenseText": "Copyright 2019 The MessagePack Community.\n\nPermission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n" 10 | }, 11 | { 12 | "name": "ccapture.js", 13 | "version": "1.0.8", 14 | "author": "Jaume Sanchez (https://www.clicktorelease.com)", 15 | "repository": "git+https://github.com/spite/ccapture.js.git", 16 | "source": "https://registry.npmjs.org/ccapture.js/-/ccapture.js-1.0.8.tgz", 17 | "license": "MIT", 18 | "licenseText": "The MIT License\n\nCopyright (c) 2012 Jaume Sanchez Elias\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE." 19 | }, 20 | { 21 | "name": "dat.gui", 22 | "version": "0.7.9", 23 | "author": "Data Arts Team, Google", 24 | "repository": "git+https://github.com/dataarts/dat.gui.git", 25 | "source": "https://registry.npmjs.org/dat.gui/-/dat.gui-0.7.9.tgz", 26 | "license": "Apache-2.0", 27 | "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2014, Google Inc.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" 28 | }, 29 | { 30 | "name": "three", 31 | "version": "0.176.0", 32 | "author": "mrdoob", 33 | "repository": "https://github.com/mrdoob/three.js", 34 | "source": "https://registry.npmjs.org/three/-/three-0.176.0.tgz", 35 | "license": "MIT", 36 | "licenseText": "The MIT License\n\nCopyright © 2010-2025 three.js authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" 37 | }, 38 | { 39 | "name": "wtd-core", 40 | "version": "3.0.0", 41 | "author": "kaisalmen (https://www.kaisalmen.de)", 42 | "repository": "https://github.com/kaisalmen/wtd", 43 | "source": "https://registry.npmjs.org/wtd-core/-/wtd-core-3.0.0.tgz", 44 | "license": "MIT", 45 | "licenseText": "MIT License\n\nCopyright (c) 2021-2024 Kai Salmen\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" 46 | }, 47 | { 48 | "name": "wtd-three-ext", 49 | "version": "3.0.0", 50 | "author": "kaisalmen (https://www.kaisalmen.de)", 51 | "repository": "https://github.com/kaisalmen/wtd", 52 | "source": "https://registry.npmjs.org/wtd-three-ext/-/wtd-three-ext-3.0.0.tgz", 53 | "license": "MIT", 54 | "licenseText": "MIT License\n\nCopyright (c) 2021-2024 Kai Salmen\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" 55 | }, 56 | { 57 | "name": "wwobjloader2", 58 | "version": "6.2.1", 59 | "author": "kaisalmen (https://www.kaisalmen.de)", 60 | "repository": "https://github.com/kaisalmen/WWOBJLoader", 61 | "source": "https://registry.npmjs.org/wwobjloader2/-/wwobjloader2-6.2.1.tgz", 62 | "license": "MIT", 63 | "licenseText": "MIT License\n\nCopyright (c) 2016-2022 Kai Salmen\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" 64 | } 65 | ] -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # MeshCat 2 | 3 | MeshCat is a remotely-controllable 3D viewer, built on top of [three.js](https://threejs.org/). The MeshCat viewer runs in a browser and listens for geometry commands over WebSockets. This makes it easy to create a tree of objects and transformations by sending the appropriate commands over the websocket. 4 | 5 | The MeshCat viewer is meant to be combined with an interface in the language of your choice. Current interfaces are: 6 | 7 | * [meshcat-python (Python 2.7 and 3.4+)](https://github.com/rdeits/meshcat-python) 8 | * [MeshCat.jl (Julia)](https://github.com/rdeits/MeshCat.jl) 9 | 10 | ## API 11 | 12 | MeshCat can be used programmatically from JS or over a WebSocket connection. 13 | 14 | ### Programmatic API 15 | 16 | To create a new MeshCat viewer, use the `Viewer` constructor: 17 | 18 | ```js 19 | let viewer = new MeshCat.Viewer(dom_element); 20 | ``` 21 | 22 | where `dom_element` is the `div` in which the viewer should live. The primary interface to the viewer is the `handle_command` function, which maps directly to the behaviors available over the WebSocket. 23 | 24 |
25 |
Viewer.handle_command(cmd)
26 |
27 | Handle a single command and update the viewer. cmd should be a JS object with at least the field type. Available command types are: 28 |
29 |
set_object
30 |
31 | Set the 3D object at a given path in the scene tree from its JSON description. Any transforms previously applied to that path will be preserved and any children of that path will continue to exist. To remove transforms and delete all children from a given path, you should send a delete command first (see below). 32 |

Internally, we append a final path segment, <object>, to the provided path before creating the object. This is done because clients may disagree about what the "intrinsic" transform of a particular geometry is (for example, is a "Box" centered on the origin, or does it have one corner at the origin?). Clients can use the matrix field of the JSON object to store that intrinsic transform, and that matrix will be preserved by attaching it to the <object> path. Generally, you shouldn't need to worry about this: if you set an object at the path /meshcat/foo, then you can set the transform at /meshcat/foo and everything will just work. 33 |

Additional fields:

34 |
35 |
path
36 |
A "/"-separated string indicating the object's path in the scene tree. An object at path "/foo/bar" is a child of an object at path "/foo", so setting the transform of (or deleting) "/foo" will also affect its children. 37 |
object
38 |
The Three.js Object, with its geometry and material, in JSON form as a JS object. The nominal format accepted is anything that ObjectLoader can handle (i.e., anything you might get by calling the toJSON() method of a Three.js Object3D). 39 |

Beyond the nominal format, Meshcat also offers a few extensions for convenience: 40 |

    41 |
  • Within the geometries stanza, the type field can be set to "_meshfile_geometry" to parse using a mesh file format. In this case, the geometry must also have a field named format set to one of "obj" or "dae" or "stl" and a field named "data" with the string contents of the file. 42 |
  • Within the geometries stanza, the type field can be set to "LineGeometry" to create lines with configurable width (works in all modern browsers). See the Line2 example below for details. 43 |
  • Within the materials stanza, the type field can be set to "_text" to use a string as the texture (i.e., a font rendered onto an image). In this case, the material must also have fields named font_size (in pixels), font_face (a string), and text (the words to render into a texture). 44 |
  • Within the materials stanza, the type field can be set to "LineMaterial" to create materials for Line2 objects with configurable line width. See the Line2 example below for details. 45 |
  • Within the inner object stanza (i.e., the object with a uuid, not the object argument to set_object), the type field can be set to "_meshfile_object" to parse using a mesh file format. In this case, the geometries and materials and geometry: {uuid} and material: {uuid} are all ignored, and the object must have a field named format set to one of "obj" or "dae" or "stl" and a field named "data" with the string contents of the file. When the format is obj, the object may also have a field named mtl_library with the string contents of the associated mtl file. 46 |
  • Within the inner object stanza, the type field can be set to "Line2" to render lines with configurable width using LineGeometry and LineMaterial. See the Line2 example below for details. 47 |
48 |
49 |

Example (nominal format): 50 |

 51 | {
 52 |     type: "set_object",
 53 |     path: "/meshcat/boxes/box1",
 54 |     object: {
 55 |         metadata: {version: 4.5, type: "Object"},
 56 |         geometries: [
 57 |             {
 58 |                 uuid: "cef79e52-526d-4263-b595-04fa2705974e",
 59 |                 type: "BoxGeometry",
 60 |                 width: 1,
 61 |                 height: 1,
 62 |                 depth:1
 63 |             }
 64 |         ],
 65 |         materials: [
 66 |             {
 67 |                 uuid: "0767ae32-eb34-450c-b65f-3ae57a1102c3",
 68 |                 type: "MeshLambertMaterial",
 69 |                 color: 16777215,
 70 |                 emissive: 0,
 71 |                 side: 2,
 72 |                 depthFunc: 3,
 73 |                 depthTest: true,
 74 |                 depthWrite: true
 75 |             }
 76 |         ],
 77 |         object: {
 78 |             uuid: "00c2baef-9600-4c6b-b88d-7e82c40e004f",
 79 |             type: "Mesh",
 80 |             geometry: "cef79e52-526d-4263-b595-04fa2705974e",
 81 |             material: "0767ae32-eb34-450c-b65f-3ae57a1102c3"
 82 |         }
 83 |     }
 84 | }
 85 |                 
86 | Note the somewhat indirect way in which geometries and materials are specified. Each Three.js serialized object has a list of geometries and a list of materials, each with a UUID. The actual geometry and material for a given object are simply references to those existing UUIDs. This enables easy re-use of geometries between objects in Three.js, although we don't really rely on that in MeshCat. Some information about the JSON object format can be found on the Three.js wiki. 87 |

88 |

Example (_meshfile_geometry): 89 |

 90 | {
 91 |     type: "set_object",
 92 |     path: "/some/file/geometry",
 93 |     object: {
 94 |         metadata: {
 95 |             version: 4.5,
 96 |             type: "Object"
 97 |         },
 98 |         geometries: [
 99 |             {
100 |                 type: "_meshfile_geometry",
101 |                 uuid: "4a08da6b-bbc6-11ee-b7a2-4b79088b524d",
102 |                 format: "obj",
103 |                 data: "v -0.06470900 ..."
104 |             }
105 |         ],
106 |         images: [
107 |             {
108 |                 uuid: "c448fc3a-bbc6-11ee-b7a2-4b79088b524d",
109 |                 url: "data:image/png;base64,iVBORw0KGgoAAA=="
110 |             }
111 |         ],
112 |         textures: [
113 |             {
114 |                 uuid: "d442ea92-bbc6-11ee-b7a2-4b79088b524d",
115 |                 wrap: [1001, 1001],
116 |                 repeat: [1, 1],
117 |                 image: "c448fc3a-bbc6-11ee-b7a2-4b79088b524d"
118 |             }
119 |         ],
120 |         materials: [
121 |             {
122 |                 uuid: "4a08da6e-bbc6-11ee-b7a2-4b79088b524d",
123 |                 type: "MeshLambertMaterial",
124 |                 color: 16777215,
125 |                 reflectivity: 0.5,
126 |                 map: "d442ea92-bbc6-11ee-b7a2-4b79088b524d"
127 |             }
128 |         ],
129 |         object: {
130 |             uuid: "4a08da6f-bbc6-11ee-b7a2-4b79088b524d",
131 |             type: "Mesh",
132 |             geometry: "4a08da6b-bbc6-11ee-b7a2-4b79088b524d",
133 |             material: "4a08da6e-bbc6-11ee-b7a2-4b79088b524d"
134 |         }
135 |     }
136 | }
137 |                 
138 |

Example (_text): 139 |

140 | {
141 |     type: "set_object",
142 |     path: "/meshcat/text",
143 |     object: {
144 |         metadata: {
145 |             version: 4.5,
146 |             type: "Object"
147 |         },
148 |         geometries: [
149 |             {
150 |                 uuid: "6fe70119-bba7-11ee-b7a2-4b79088b524d",
151 |                 type: "PlaneGeometry",
152 |                 width: 8,
153 |                 height: 8,
154 |                 widthSegments: 1,
155 |                 heightSegments: 1
156 |             }
157 |         ],
158 |         textures: [
159 |             {
160 |                 uuid: "0c8c99a8-bba8-11ee-b7a2-4b79088b524d",
161 |                 type: "_text",
162 |                 text: "Hello, world!",
163 |                 font_size: 300,
164 |                 font_face: "sans-serif"
165 |             }
166 |         ],
167 |         materials: [
168 |             {
169 |                 uuid: "6fe7011b-bba7-11ee-b7a2-4b79088b524d",
170 |                 type: "MeshPhongMaterial",
171 |                 transparent: true,
172 |                 map: "0c8c99a8-bba8-11ee-b7a2-4b79088b524d",
173 |             }
174 |         ],
175 |         object: {
176 |             uuid: "6fe7011c-bba7-11ee-b7a2-4b79088b524d",
177 |             type: "Mesh",
178 |             geometry: "6fe70119-bba7-11ee-b7a2-4b79088b524d",
179 |             material: "6fe7011b-bba7-11ee-b7a2-4b79088b524d",
180 |         }
181 |     }
182 | }
183 |                 
184 |

Example (_meshfile_object): 185 |

186 | {
187 |     type: "set_object",
188 |     path: "/meshcat/wavefront_file",
189 |     object: {
190 |         metadata: {version: 4.5, type: "Object"},
191 |         object: {
192 |             uuid: "00c2baef-9600-4c6b-b88d-7e82c40e004f",
193 |             type: "_meshfile_object",
194 |             format: "obj",
195 |             data: "mtllib ./cube.mtl\nusemtl material_0\nv 0.0 0.0 0.0 ...",
196 |             mtl_library: "newmtl material_0\nKa 0.2 0.2 0.2\n ...",
197 |             resources: {"cube.png": "data:image/png;base64,iV ..."}
198 |         }
199 |     }
200 | }
201 |                 
202 |

Check test/meshfile_object_obj.html for the full demo.

203 |

Example (Line2 with LineGeometry and LineMaterial):

204 |

Modern browsers don't support the GL_LINEWIDTH parameter, so standard Line objects won't show varying line widths. MeshCat supports Line2 objects with LineGeometry and LineMaterial for lines with configurable properties.

205 |
206 | {
207 |     type: "set_object",
208 |     path: "/meshcat/fatline",
209 |     object: {
210 |         metadata: {version: 4.5, type: "Object"},
211 |         geometries: [
212 |             {
213 |                 uuid: "6fe70119-bba7-11ee-b7a2-4b79088b524d",
214 |                 type: "LineGeometry",
215 |                 position: {
216 |                     array: new Float32Array([0, 0, 0, 1, 0, 0, 1, 1, 0])
217 |                 },
218 |                 color: {
219 |                     array: new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1])
220 |                 }
221 |             }
222 |         ],
223 |         materials: [
224 |             {
225 |                 uuid: "6fe7011b-bba7-11ee-b7a2-4b79088b524d",
226 |                 type: "LineMaterial",
227 |                 color: 16777215,
228 |                 linewidth: 5.0,
229 |                 vertexColors: true,
230 |                 dashed: false,
231 |                 worldUnits: false
232 |             }
233 |         ],
234 |         object: {
235 |             uuid: "6fe7011c-bba7-11ee-b7a2-4b79088b524d",
236 |             type: "Line2",
237 |             geometry: "6fe70119-bba7-11ee-b7a2-4b79088b524d",
238 |             material: "6fe7011b-bba7-11ee-b7a2-4b79088b524d"
239 |         }
240 |     }
241 | }
242 |                 
243 |

LineGeometry supports these attributes:

244 |
    245 |
  • position: Float32Array of vertex positions (x, y, z, x, y, z, ...) 246 |
  • color: (optional) Float32Array of per-vertex RGB colors (r, g, b, r, g, b, ...) 247 |
248 |

LineMaterial supports the exact same attributes as documented in the Three.js LineMaterial docs.

249 |

Check test/fatlines.html for a full demo.

250 |
251 |
set_transform
252 |
253 | Set the homogeneous transform for a given path in the scene tree. An object's pose is the concatenation of all of the transforms along its path, so setting the transform of "/foo" will move the objects at "/foo/box1" and "/foo/robots/HAL9000". 254 |

Additional fields:

255 |
256 |
path
257 |
A "/"-separated string indicating the object's path in the scene tree. An object at path "/foo/bar" is a child of an object at path "/foo", so setting the transform of (or deleting) "/foo" will also affect its children. 258 |
matrix
259 |
260 | The homogeneous transformation matrix, given as a 16-element Float32Array in column-major order. 261 |
262 |
263 | Example: 264 |
265 | {
266 |     type: "set_transform",
267 |     path: "/meshcat/boxes",
268 |     matrix: new Float32Array([1, 0, 0, 0,
269 |                             0, 1, 0, 0,
270 |                             0, 0, 1, 0,
271 |                             0.5, 0.0, 0.5, 1])
272 | }
273 |                 
274 |
275 |
delete
276 |
277 | Delete the object at the given path as well as all of its children. 278 |

Additional fields:

279 |
280 |
path
281 |
A "/"-separated string indicating the object's path in the scene tree. An object at path "/foo/bar" is a child of an object at path "/foo", so setting the transform of (or deleting) "/foo" will also affect its children. 282 |
283 | Example: 284 |
285 | {
286 |     type: "delete",
287 |     path: "/meshcat/boxes"
288 | }
289 |                 
290 |
291 |
set_property
292 |
293 | Set a single named property of the object at the given path. If no object exists at that path, an empty one is automatically created. 294 |

Note: as we append an extra path element with the name <object> to every item created with set_object, if you want to modify a property of the object itself, rather than the group containing it, you should ensure that your path is of the form /meshcat/foo/<object>

295 |

Additional fields:

296 |
297 |
property
298 |
299 | The name of the property to set, as a string. The following properties are convenience properties. Meshcat provides a mapping from these *names* to various properties contained throughout its scene graph: 300 |
    301 |
  • visible: bool 302 |
  • position: number[3] 303 |
  • quaternion: number[4] 304 |
  • scale: number[3] 305 |
  • color: number[4] 306 |
  • opacity: number (this is the same as the 4th element of color) 307 |
  • modulated_opacity: number 308 |
  • top_color: number[3] (only for the Background) 309 |
  • bottom_color: number[3] (only for the Background) 310 |
311 | Properties not on the above list will be set directly on the THREE.Object3D object. This provides a powerful capability to customize the scene, but should be considered an advanced usage -- you're on your own to avoid any unwanted side-effects. 312 |

313 | Properties can be *chained*. For example, for an object with a phong material (MeshPhongMaterial), we may want to tweak its shininess, making it duller. Shininess is not a property of the object itself, but the object's material. There is no *path* to that material, but the property name can include a property name chain, e.g., `material.shininess`. While setting the property, the chained properties will be evaluated in sequence, such that the final name in the chain is the property that receives the `value`. 314 |

315 | As noted, specifying a `path` that doesn't exist creates that path. However, specifying a property that doesn't exist does *not* create that property. If a name in the property chain is missing, an error message will be printed to the console and no value will be assigned. This is not a no-op per se. If the `path` led to the implicit creation of a new folder and object, that pair will still be in place. 316 |

317 | More subtly, if the property name chain has an interior name (e.g., the `foo` in `material.foo.color`) that exists but is not an object and does not have properties (such as if `foo` were a `Number`), then, again, an error gets written to the console and no value will be assigned. 318 |

319 | Finally, property chains can include arrays, such as `"children[1].material.specular"`. The index will be evaluated as a property (with all of the potential consequences as outlined above). In error messages, it may be reported as `children.1` instead of `children[1]`. 320 |

321 |
value
322 |
The new value.
323 |
324 | Example 1: 325 |
326 | {
327 |     type: "set_property",
328 |     path: "/Cameras/default/rotated/<object>",
329 |     property: "zoom",
330 |     value: 2.0
331 | }
332 |                 
333 | Example 2: 334 |
335 | {
336 |     type: "set_property",
337 |     path: "/Lights/DirectionalLight/<object>",
338 |     property: "intensity",
339 |     value: 1.0
340 | }
341 |                 
342 | Example 3 (chained properties): 343 |
344 | {
345 |     type: "set_property",
346 |     path: "/Lights/SpotLight/<object>",
347 |     property: "shadow.radius",
348 |     value: 1.0
349 | }
350 |                 
351 |
352 |
set_animation
353 |
354 | Create an animation of any number of properties on any number of objects in the scene tree, and optionally start playing that animation. 355 |

Additional fields:

356 |
357 |
animations
358 |
359 | A list of objects, each with two fields: 360 |
361 |
path
362 |
363 | The path to the object whose property is begin animated. As with set_property above, you will need to append <object> to the path to set an object's intrinsic property. 364 |
365 |
clip
366 |
367 | A Three.js AnimationClip in JSON form. The clip in turn has the following fields: 368 |
369 |
fps
370 |
371 | The frame rate of the clip 372 |
373 |
name
374 |
375 | A name for this clip. Not currently used. 376 |
377 |
tracks
378 |
379 | The tracks (i.e. the properties to animate for this particular object. In Three.js, it is possible for a track to specify the name of the object it is attached to, and Three.js will automatically perform a depth-first search for a child object with that name. We choose to ignore that feature, since MeshCat already has unambiguous paths. So each track should just specify a property in its name field, with a single "." before that property name to signify that it applies to exactly the object given by the path above. 380 |

Each track has the following fields:

381 |
382 |
name
383 |
384 | The property to be animated, with a leading "." (e.g. ".position") 385 |
386 |
type
387 |
388 | The Three.js data type of the property being animated (e.g. "vector3" for the position property) 389 |
390 |
keys
391 |
392 | The keyframes of the animation. The format is a list of objects, each with a field time (in frames) and value indicating the value of the animated property at that time. 393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
options
401 |
402 | Additional options controlling the animation. Currently supported values are: 403 |
404 |
play
405 |
Boolean [true]. Controls whether the animation should play immediately.
406 |
repetitions
407 |
Integer [1]. Controls the number of repetitions of the animation each time you play it.
408 |
409 |
410 |
411 | Example: 412 |
413 | {
414 |     type: "set_animation",
415 |     animations: [{
416 |             path: "/Cameras/default",
417 |             clip: {
418 |                 fps: 30,
419 |                 name: "default",
420 |                 tracks: [{
421 |                     name: ".position"
422 |                     type: "vector3",
423 |                     keys: [{
424 |                         time: 0,
425 |                         value: [0, 1, .3]
426 |                     },{
427 |                         time: 80,
428 |                         value: [0, 1, 2]
429 |                     }],
430 |                 }]
431 |             }
432 |         },{
433 |             path: "/meshcat/boxes",
434 |             clip: {
435 |                 fps: 30,
436 |                 name: "default",
437 |                 tracks: [{
438 |                     name: ".position"
439 |                     type: "vector3",
440 |                     keys: [{
441 |                         time: 0,
442 |                         value: [0, 1, 0]
443 |                     },{
444 |                         time: 80,
445 |                         value: [0, -1, 0]
446 |                     }],
447 |                 }]
448 |             }
449 |         }],
450 |     options: {
451 |         play: true,
452 |         repetitions: 1
453 |     }
454 | }
455 |                 
456 |
457 |
set_target
458 |
459 | Set the target of the 3D camera, around which it rotates. This is expressed in a left-handed coordinate system where y is up. 460 |

Example:

461 |
462 | {
463 |     "type": "set_target",
464 |     value: [0., 1., 0.]
465 | }
466 |                 
467 | This sets the camera target to `(0, 1, 0)` 468 |
469 |
capture_image
470 |
471 | Capture an image from the viewport. At the moment it will return the image at a provided resolution (by default 1920x1080). 472 |
473 | {
474 |     "type": "capture_image",
475 |     "xres": 1920,
476 |     "yres": 1080
477 | }
478 |                 
479 | This sets the camera target to `(0, 1, 0)` 480 |
481 |
set_render_callback
482 |
483 | Each render loop updates the camera, renders the scene and updates 484 | animation. Between updating the camera and rendering the scene, 485 | the Viewer invokes a user-configurable callback. This callback 486 | can be used to execute arbitrary code per rendered frame. 487 |

488 | The callback is evaluated in a scope such that the Viewer 489 | instance is available as `this`. In declaring the 490 | command, the callback should be a *string* that gets evaluated 491 | into a function. Passing the string "null" will restore the 492 | render callback to being its default no-op function. 493 |

494 | For example, the following is an example callback that dispatches 495 | the camera's pose in the world to the open websocket connection. 496 |
497 | {
498 |     "type": "set_render_callback",
499 |     "callback": `() => {
500 |         if (this.is_perspective()) {
501 |             if (this.connection.readyState == 1 /* OPEN */) {
502 |                 this.connection.send(msgpack.encode({
503 |                     'type': 'camera_pose',
504 |                     'camera_pose': this.camera.matrixWorld.elements
505 |                 }));
506 |             }
507 |         }
508 |     }`
509 | }
510 |             
511 | This dispatches the camera's pose in the world to the websocket 512 | connection. 513 |
514 |
515 |
516 |
517 | 518 | ### WebSocket API 519 | 520 |
521 |
Viewer.connect(url)
522 |
523 | Set up a web socket connection to a server at the given URL. The viewer will listen for messages on the socket as binary MsgPack blobs. Each message will be decoded using msgpack.decode() from msgpack-javascript and the resulting object will be passed directly to Viewer.handle_command() as documented above. 524 |

525 | Note that we do support the MsgPack extension types listed in msgpack-javascript#extension-types, with additional support for the Float32Array type which is particularly useful for efficiently sending point data and for Uint32Array, Uint8Array, and Int32Array. 526 |

527 |
528 | 529 | ### Setting opacity 530 | 531 | Objects can have their opacity changed in the obvious way, i.e.: 532 | 533 |
534 | {
535 |     type: "set_property",
536 |     path: "/path/to/my/geometry",
537 |     property: "opacity",
538 |     value: 0.5
539 | }
540 | 
541 | 542 | This would assign the opacity value 0.5 to *all* of the materials found rooted 543 | at `"/path/to/my/geometry"`. (That means using the path `"/path"` could affect 544 | many geometries.) However, this will overwrite whatever opacity the geometry had 545 | inherently (i.e., from the material defined in the mesh file); a transparent 546 | object could become *more* opaque. 547 | 548 | Meshcat offers a pseudo property "modulated_opacity". Meshcat remembers an 549 | object's inherent opacity and, by setting *this* value, sets the rendered 550 | opacity to be the product of the inherent and modulated opacity value. The 551 | corresponding command would be: 552 | 553 |
554 | {
555 |     type: "set_property",
556 |     path: "/path/to/my/geometry",
557 |     property: "modulated_opacity",
558 |     value: 0.5
559 | }
560 | 
561 | 562 | Setting `"modulated_opacity"` to 1 will restore the geometry's original 563 | opacity. This does *not* introduce a queryable `modulated_opacity` property on 564 | any material. This is what makes it a "pseudo" property. The same tree-based 565 | scope of effect applies to `"modulated_opacity"` as with `"opacity"` (and also 566 | the `"color"` property). 567 | 568 | Meshcat *always* remembers the inherent opacity value. So, if you've overwritten 569 | the value (via setting `"opacity"` or `"color"`), you can restore it by setting 570 | the `"modulated_opacity"` value to 1.0. 571 | 572 | ### Useful Paths 573 | 574 | The default MeshCat scene comes with a few objects at pre-set paths. You can replace, delete, or transform these objects just like anything else in the scene. 575 | 576 |
577 |
/Lights/DirectionalLight
578 |
The single directional light in the scene.
579 |
/Lights/AmbientLight
580 |
The ambient light in the scene.
581 |
/Grid
582 |
The square grid in the x-y plane, with 0.5-unit spacing.
583 |
/Axes
584 |
The red, green, and blue XYZ triad at the origin of the scene (invisible by default, click on "open controls" in the upper right to toggle its visibility).
585 |
/Cameras
586 |
The camera from which the scene is rendered (see below for details)
587 |
/Background
588 |
The background texture, with properties for "top_color" and "bottom_color" as well as a boolean "visible".
589 |
/Render Settings/ 590 |
Contains properties for how the overall scene renders in your current session. This is not a 3D element in the scene, but provides parameters for configuring the renderer.
591 | 592 | 593 | ### Camera Control 594 | 595 | The camera is just another object in the MeshCat scene, so you can move it around with set_transform commands like any other object. You can also use set_target to change the camera's target. Please note that replacing the camera with set_object is not currently supported (but we expect to implement this in the future). 596 | 597 | Controlling the camera is slightly more complicated than moving a single object because the camera actually has two important poses: the origin about which the camera orbits when you click-and-drag with the mouse, and the position of the camera itself. In addition, cameras and controls in Three.js assume a coordinate system in which the Y axis is upward. In robotics, we typically have the Z axis pointing up, and that's what's done in MeshCat. To account for this, the actual camera lives inside a few path elements: 598 | 599 | `/Cameras/default/rotated/` 600 | 601 | The /rotated path element exists to remind users that its transform has been rotated to a Y-up coordinate system for the camera inside. 602 | 603 | There is one additional complication: the built-in orbit and pan controls (which allow the user to move the view with their mouse) use the translation of *only* the intrinsic transform of the camera object itself to determine the radius of the orbit. That means that, in practice, you can allow the user to orbit by setting the `position` property at the path `/Cameras/default/rotated/` to a nonzero value like `[2, 0, 0]`, or you can lock the orbit controls by setting the `position` property at that path to `[0, 0, 0]`. Remember that whatever translation you choose is in the *rotated*, Y-up coordinate system that the Three.js camera expects. We're sorry. 604 | 605 | #### Examples 606 | 607 | To move the camera's center of attention to the point `[1, 2, 3]`, while still allowing the user to orbit and pan manually, we suggest setting the transform of the `/Cameras/default` path to whatever center point you want and setting the `position` property of `/Cameras/default/rotated/` to `[2, 0, 0]`. That means sending two commands: 608 | 609 | ```js 610 | { 611 | type: "set_transform", 612 | path: "/Cameras/default", 613 | matrix: new Float32Array([1, 0, 0, 0, 614 | 0, 1, 0, 0, 615 | 0, 0, 1, 0, 616 | 1, 2, 3, 1]) // the translation here is the camera's 617 | // center of attention 618 | }, 619 | { 620 | type: "set_property", 621 | path: "/Cameras/default/rotated/", 622 | property: "position", 623 | value: [2, 0, 0] // the offset of the camera about its point of rotation 624 | } 625 | ``` 626 | 627 | To move the camera itself to the point `[1, 2, 3]` and lock its controls, we suggest setting the transform of `/Cameras/default` to the exact camera pose and setting the `position` property of `/Cameras/default/rotated/` to `[0, 0, 0]`: 628 | 629 | ```js 630 | { 631 | type: "set_transform", 632 | path: "/Cameras/default", 633 | matrix: new Float32Array([1, 0, 0, 0, 634 | 0, 1, 0, 0, 635 | 0, 0, 1, 0, 636 | 1, 2, 3, 1]) // the translation here is now the camera's 637 | // exact pose 638 | }, 639 | { 640 | type: "set_property", 641 | path: "/Cameras/default/rotated/", 642 | property: "position", 643 | value: [0, 0, 0] // set to zero to lock the camera controls 644 | } 645 | ``` 646 | 647 | ## Developing MeshCat 648 | 649 | The MeshCat javascript sources live in `src/index.js`. We use [webpack](https://webpack.js.org/) to bundle up MeshCat with its Three.js dependencies into a single javascript bundle. If you want to edit the MeshCat source, you'll need to regenerate that bundle. Fortunately, it's pretty easy: 650 | 651 | ### Option 1: Use Docker 652 | 653 | If you have Docker + BuildKit installed, then you can run `rebuild/rebuild.sh` 654 | to hermetically regenerate the `dist` files. The very first time you run it, it 655 | might take a little time to download all of the relevant files. Subsequent runs 656 | should be much faster (~5-10 seconds). 657 | 658 | This option offers the easiest way to rebuild, with the drawback that "watch" 659 | mode of webpack (to automatically rebuild every time you save) is unavailable. 660 | 661 | To install Docker + BuildKit on Ubuntu, run: 662 | 663 | ``` 664 | sudo apt install docker.io docker-buildx 665 | ``` 666 | 667 | To run as non-root, you will probably also need to update the `docker` group 668 | permissions: 669 | 670 | https://docs.docker.com/engine/install/linux-postinstall/ 671 | 672 | ### Option 2: Install Yarn + NPM tools 673 | 674 | This option offers a convenient "watch" mode during an edit-test development 675 | cycle, but if you're not careful when installing the yarn+node+npm tools you 676 | might damage other software on your computer. 677 | 678 | 1. Install [yarn](https://yarnpkg.com/en/docs/install). This should also install `node` and `npm`. Try running `yarn -v` and `npm -v` to make sure those programs are installed. 679 | 2. Run `yarn` 680 | * This will read the `project.json` file and install all the necessary javascript dependencies. 681 | 3. Run `yarn build` 682 | * This will run webpack and create the bundled output in `dist/main.min.js`. The build script will also watch for changes to the MeshCat source files and regenerate the bundle whenever those source files change. 683 | 4. Try it out! You can load the bundled `main.min.js` in your own application, or you can open up `dist/index.html` in your browser. 684 | 685 | Note that due to caching, you may need to do a hard refresh (shift+F5 or ctrl+shift+R) in your browser to reload the updated javascript bundle. 686 | --------------------------------------------------------------------------------