{"id":187,"date":"2025-10-23T13:47:25","date_gmt":"2025-10-23T13:47:25","guid":{"rendered":"https:\/\/leonwoud.com\/devblog\/?p=187"},"modified":"2025-10-23T13:53:52","modified_gmt":"2025-10-23T13:53:52","slug":"so-long-cube","status":"publish","type":"post","link":"https:\/\/leonwoud.com\/devblog\/so-long-cube\/","title":{"rendered":"So long, Cube"},"content":{"rendered":"\n<p>It&#8217;s time to look into getting different geometry into our scene.<\/p>\n\n\n\n<p>In the Vulkan version of StaticRectangle, I was using <code>obj<\/code> files for meshes and a 3rd party library called <code>tinyobjloader<\/code> to load them. This was fine for that application because the size of the files wasn&#8217;t much of a concern.<\/p>\n\n\n\n<p>I mean it&#8217;s always a concern, but it wasn&#8217;t as much of a concern in the early stages. It&#8217;s something that can be optimised later.<sup data-fn=\"4f8a7e3b-6d77-43b2-b663-60d393b35b20\" class=\"fn\"><a id=\"4f8a7e3b-6d77-43b2-b663-60d393b35b20-link\" href=\"#4f8a7e3b-6d77-43b2-b663-60d393b35b20\">1<\/a><\/sup><\/p>\n\n\n\n<p>Now we&#8217;re on the web, size is very much a concern as we&#8217;ll be expecting the client to download these resources.<\/p>\n\n\n\n<p>Up until now, my geometry has been defined in JavaScript<sup data-fn=\"3e153b78-f939-4e9c-a45d-e823b1c69c6f\" class=\"fn\"><a id=\"3e153b78-f939-4e9c-a45d-e823b1c69c6f-link\" href=\"#3e153b78-f939-4e9c-a45d-e823b1c69c6f\">2<\/a><\/sup><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:1rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.5rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\/**\n * Create simple cube geometry.\n * \n * @returns Cube geometry with size 1x1x1\n *\/\nexport function ExampleCube(): Geometry {\n    const positions = new Float32Array(&#91;\n        \/\/ Front\n        -0.5, -0.5, 0.5,\n        0.5, -0.5, 0.5,\n        -0.5, 0.5, 0.5,\n        0.5, 0.5, 0.5,\n        \/\/ Top\n        -0.5, 0.5, 0.5,\n        0.5, 0.5, 0.5,\n        -0.5, 0.5, -0.5,\n        0.5, 0.5, -0.5,\n        \/\/ Back\n        -0.5, 0.5, -0.5,\n        0.5, 0.5, -0.5,\n        -0.5, -0.5, -0.5,\n        0.5, -0.5, -0.5,\n        \/\/ Bottom\n        -0.5, -0.5, -0.5,\n        0.5, -0.5, -0.5,\n        -0.5, -0.5, 0.5,\n        0.5, -0.5, 0.5,\n        \/\/ Right\n        0.5, -0.5, 0.5,\n        0.5, -0.5, -0.5,\n        0.5, 0.5, 0.5,\n        0.5, 0.5, -0.5,\n        \/\/ Left\n        -0.5, -0.5, -0.5,\n        -0.5, -0.5, 0.5,\n        -0.5, 0.5, -0.5,\n        -0.5, 0.5, 0.5\n    &#93;);\n\n    const indices = new Uint16Array(&#91;\n        0, 1, 2, 2, 1, 3,       \/\/ Front\n        4, 5, 6, 6, 5, 7,       \/\/ Top\n        8, 9, 10, 10, 9, 11,    \/\/ Back\n        12, 13, 14, 14, 13, 15, \/\/ Bottom\n        16, 17, 18, 18, 17, 19, \/\/ Right\n        20, 21, 22, 22, 21, 23  \/\/ Left\n    &#93;);\n\n    const colors = new Uint8Array(&#91;\n        \/\/ Front - Blue\n        0, 0, 255, 255,\n        0, 0, 255, 255,\n        0, 0, 255, 255,\n        0, 0, 255, 255,\n        \/\/ Top - Green\n        0, 255, 0, 255,\n        0, 255, 0, 255,\n        0, 255, 0, 255,\n        0, 255, 0, 255,\n        \/\/ Back - Blue\n        0, 0, 255, 255,\n        0, 0, 255, 255,\n        0, 0, 255, 255,\n        0, 0, 255, 255,\n        \/\/ Bottom - Green\n        0, 255, 0, 255,\n        0, 255, 0, 255,\n        0, 255, 0, 255,\n        0, 255, 0, 255,\n        \/\/ Right - Red\n        255, 0, 0, 255,\n        255, 0, 0, 255,\n        255, 0, 0, 255,\n        255, 0, 0, 255,\n        \/\/ Left - Red\n        255, 0, 0, 255,\n        255, 0, 0, 255,\n        255, 0, 0, 255,\n        255, 0, 0, 255\n    &#93;);<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #616E88\">\/**<\/span><\/span>\n<span class=\"line\"><span style=\"color: #616E88\"> * Create simple cube geometry.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #616E88\"> * <\/span><\/span>\n<span class=\"line\"><span style=\"color: #616E88\"> * <\/span><span style=\"color: #ECEFF4\">@<\/span><span style=\"color: #8FBCBB\">returns<\/span><span style=\"color: #616E88\"> Cube geometry with size 1x1x1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #616E88\"> *\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">export<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">function<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">ExampleCube<\/span><span style=\"color: #ECEFF4\">()<\/span><span style=\"color: #81A1C1\">:<\/span><span style=\"color: #D8DEE9FF\"> Geometry <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">const<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">positions<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Float32Array<\/span><span style=\"color: #D8DEE9FF\">(&#91;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Front<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Top<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Back<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Bottom<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Right<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Left<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.5<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    &#93;)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">const<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">indices<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Uint16Array<\/span><span style=\"color: #D8DEE9FF\">(&#91;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">2<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">2<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">3<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\">       <\/span><span style=\"color: #616E88\">\/\/ Front<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">4<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">6<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">6<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">5<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">7<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\">       <\/span><span style=\"color: #616E88\">\/\/ Top<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">8<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">9<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">10<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">10<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">9<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">11<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #616E88\">\/\/ Back<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">12<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">13<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">14<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">14<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">13<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">15<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #616E88\">\/\/ Bottom<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">16<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">17<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">18<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">18<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">17<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">19<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #616E88\">\/\/ Right<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">20<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">21<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">22<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">22<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">21<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">23<\/span><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #616E88\">\/\/ Left<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    &#93;)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">const<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">colors<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Uint8Array<\/span><span style=\"color: #D8DEE9FF\">(&#91;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Front - Blue<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Top - Green<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Back - Blue<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Bottom - Green<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Right - Red<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Left - Red<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    &#93;)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>This geometry has no UVs (so could not receive a texture) and no normals (so could not be lit).<\/p>\n\n\n\n<p>While certainly possible to write the other vertex attributes by hand (in fact, I did) it should be fairly obvious that it isn&#8217;t something you&#8217;d do for more complex geometry\u2014even the six-sided cube is tedious.<\/p>\n\n\n\n<p>To define more complex geometry, we need to move past handwritten arrays and think about converting\/exporting geometry from a different source.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Geometry Interface<\/h2>\n\n\n\n<p>Before we do that, let&#8217;s look at the <code>Geometry<\/code> interface currently in StaticRectangle<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:1rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.5rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\/**\n * Geometry data used to create a Mesh.\n * Includes vertex positions, optional UVs, normals, colors, and indices.\n *\/\nexport interface Geometry {\n    positions: Float32Array; \/\/ Vertex positions\n    uvs?: Float32Array;      \/\/ Optional texture coordinates\n    normals?: Float32Array;  \/\/ Optional vertex normals\n    colors?: Uint8Array;     \/\/ Vertex colors as Uint8Array\n    indices?: Uint16Array | Uint32Array;   \/\/ Optional index buffer\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #616E88\">\/**<\/span><\/span>\n<span class=\"line\"><span style=\"color: #616E88\"> * Geometry data used to create a Mesh.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #616E88\"> * Includes vertex positions, optional UVs, normals, colors, and indices.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #616E88\"> *\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">export<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">interface<\/span><span style=\"color: #D8DEE9FF\"> Geometry <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    positions<\/span><span style=\"color: #81A1C1\">:<\/span><span style=\"color: #D8DEE9FF\"> Float32Array<\/span><span style=\"color: #81A1C1\">;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #616E88\">\/\/ Vertex positions<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    uvs<\/span><span style=\"color: #81A1C1\">?:<\/span><span style=\"color: #D8DEE9FF\"> Float32Array<\/span><span style=\"color: #81A1C1\">;<\/span><span style=\"color: #D8DEE9FF\">      <\/span><span style=\"color: #616E88\">\/\/ Optional texture coordinates<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    normals<\/span><span style=\"color: #81A1C1\">?:<\/span><span style=\"color: #D8DEE9FF\"> Float32Array<\/span><span style=\"color: #81A1C1\">;<\/span><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #616E88\">\/\/ Optional vertex normals<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    colors<\/span><span style=\"color: #81A1C1\">?:<\/span><span style=\"color: #D8DEE9FF\"> Uint8Array<\/span><span style=\"color: #81A1C1\">;<\/span><span style=\"color: #D8DEE9FF\">     <\/span><span style=\"color: #616E88\">\/\/ Vertex colors as Uint8Array<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    indices<\/span><span style=\"color: #81A1C1\">?:<\/span><span style=\"color: #D8DEE9FF\"> Uint16Array <\/span><span style=\"color: #81A1C1\">|<\/span><span style=\"color: #D8DEE9FF\"> Uint32Array<\/span><span style=\"color: #81A1C1\">;<\/span><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #616E88\">\/\/ Optional index buffer<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The only requirement to create <code>Geometry<\/code> is vertex positions and we expect them as a flat <code>Float32Array<\/code>.<\/p>\n\n\n\n<p>There are different options we can define on the pipeline about how to treat this flat array of positions, by default it&#8217;s treated as a <code>triangle-list<\/code><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><code>\"triangle-list\"<\/code>: Each consecutive triplet of three vertices defines a triangle primitive.<\/p>\n<\/blockquote>\n\n\n\n<p>So, every three values in our array form a corner of a triangle, there are three corners to a triangle, two triangles in a face and six faces on a cube.<\/p>\n\n\n\n<p><code>3 * 3 * 2 * 6 = 108<\/code><\/p>\n\n\n\n<p>Hang on, the positions array in our example cube isn&#8217;t 108 values, it&#8217;s 72! (just trust me, or don&#8217;t&#8230; you can count them if you like)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Indices<\/h2>\n\n\n\n<p>The reason we are able to define less positions than the 108 we calculated, is because many of these vertices are sitting on top of each other. If you consider the first line of the indices array which defines our &#8216;front&#8217; face<\/p>\n\n\n\n<p><code>0, 1, 2, 2, 1, 3, \/\/ Front<\/code><\/p>\n\n\n\n<figure class=\"wp-block-image size-medium\"><img fetchpriority=\"high\" decoding=\"async\" width=\"300\" height=\"300\" src=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/indices-300x300.png\" alt=\"\" class=\"wp-image-201\" srcset=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/indices-300x300.png 300w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/indices-150x150.png 150w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/indices-48x48.png 48w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/indices-250x250.png 250w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/indices-180x180.png 180w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/indices-500x500.png 500w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/indices.png 506w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/figure>\n\n\n\n<p>we only need these 4 positions to draw the two triangles that make up the face. But what about the top, sides and bottom? can we not also share those positions too, then we&#8217;d only have 8 vertices and that is less than the 24.<\/p>\n\n\n\n<p>Yep, we could do that\u2014an optimisation for later perhaps. It would be interesting to see how much savings you get in storage vs CPU time to expand for the GPU. I have decided to keep it simple and compress the files instead, which in essence is a similar thing. I&#8217;m sure I&#8217;ll do a number of optimisation posts in the future.<\/p>\n\n\n\n<p>So&#8230; what happens if you don&#8217;t include an indices array?<\/p>\n\n\n\n<p>If you don&#8217;t include indices, then it&#8217;s just assumed that what are you providing are fully expanded vertex positions (sometimes referred to as polygon\/triangle soup), so for the cube we&#8217;d be expecting 36 positions (108 elements) and apart from the obvious x, y z triplets the ordering doesn&#8217;t matter. There is a different draw call you make for this, in WebGPU it&#8217;s <code>draw<\/code> vs <code>drawIndexed<\/code>. There are times when you don&#8217;t bother with an index buffer, something I&#8217;ll get into in the future when it comes to particle systems and dive into render \/ storage optimisations.<\/p>\n\n\n\n<p>In case you were curious, this is what happens if you give our non-expanded positions array for the cube to the <code>draw<\/code> command sans index buffer.<\/p>\n\n\n\n<figure class=\"wp-block-image size-medium\"><img decoding=\"async\" width=\"300\" height=\"266\" src=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-300x266.png\" alt=\"\" class=\"wp-image-190\" srcset=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-300x266.png 300w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-250x222.png 250w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-550x488.png 550w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-203x180.png 203w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-338x300.png 338w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-563x500.png 563w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image.png 660w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/figure>\n\n\n\n<p>Which is actually one of the early bugs I ran into getting my handwritten arrays to render.<\/p>\n\n\n\n<p>Quick note before we move on, the order in which these indices define the triangles is important, or at least, the &#8216;winding order&#8217; is important to our indices. If you look at the indices of the front face, you may notice they are going in a clockwise direction. This lets the renderer know which side of the face is considered the front. We use this for a common rendering optimisation called back face culling to cull faces that are facing away from you.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Preparing to export Geometry<\/h2>\n\n\n\n<p>I know I said I was done with the cube but let&#8217;s dust it off one last time. The reason it&#8217;s such a great example is because of how simple it is to understand.<\/p>\n\n\n\n<p>Here we are in Autodesk Maya, I have created the same 1x1x1 cube that I had previously defined by hand.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img decoding=\"async\" width=\"1024\" height=\"1010\" src=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3-1024x1010.png\" alt=\"\" class=\"wp-image-193\" style=\"width:500px\" srcset=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3-1024x1010.png 1024w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3-300x296.png 300w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3-768x758.png 768w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3-48x48.png 48w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3-250x247.png 250w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3-550x543.png 550w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3-800x789.png 800w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3-182x180.png 182w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3-304x300.png 304w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3-507x500.png 507w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/image-3.png 1184w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>In order to export the geometry, we need to iterate over the mesh to pull out positions, normals, uvs and vertex colours. Maya provides multiple ways to do this, but I&#8217;ll use the <code>maya.api.OpenMaya<\/code> API in Python.<\/p>\n\n\n\n<p>First, we&#8217;ll define the same Geometry interface (using <code>dataclasses.dataclass<\/code>)<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:1rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.5rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>from dataclasses import dataclass\n\n@dataclass\nclass Geometry:\n    positions: list&#91;float&#93;\n    normals: list&#91;float&#93;\n    uvs: list&#91;float&#93;\n    colors: list&#91;int&#93;\n    indices: list&#91;int&#93;<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">from<\/span><span style=\"color: #D8DEE9FF\"> dataclasses <\/span><span style=\"color: #81A1C1\">import<\/span><span style=\"color: #D8DEE9FF\"> dataclass<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">@<\/span><span style=\"color: #D08770\">dataclass<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">class<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">Geometry<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    positions<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> list<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #88C0D0\">float<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    normals<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> list<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #88C0D0\">float<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    uvs<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> list<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #88C0D0\">float<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    colors<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> list<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #88C0D0\">int<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    indices<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> list<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #88C0D0\">int<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The next part got complicated thanks to UVs and the myriads of options we have to define them in 3d modelling software. Not so much for my simple cube example, but I intend to export more than just that.<\/p>\n\n\n\n<p>The majority of this is really just Maya implementation detail, what is important is that I am able to iterate over the polygons in a mesh\u2014a polygon could be quads, triangles or n-gons, doesn&#8217;t matter thanks to <code>getPolygonTriangleVertices<\/code>\u2014grab all the details I need for that polygon, and eventually pop out an instance of the <code>Geometry<\/code> dataclass.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:1rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.5rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>import maya.api.OpenMaya as om\n\n\ndef convert_to_geometry(object_path: str) -> Geometry:\n    \"\"\" Converts the given Maya object to Geometry for\n    export to StaticRectangle\n    \"\"\"\n    # Get the mesh\n    sel = om.MSelectionList()\n    sel.add(object_path)\n    dag_path = sel.getDagPath(0)\n    mesh_fn = om.MFnMesh(dag_path)\n    poly_iter = om.MItMeshPolygon(dag_path)\n    # getPoints returns an MPointArray of deduped vertices\n    point_array = mesh_fn.getPoints(om.MSpace.kObject)\n    positions = []\n    normals = []\n    uvs = []\n    colors = []\n    indices = []\n    # vertex_map&#91;(global_idx , uv_id , normal_id )&#93; = expanded vertex index\n    vertex_map = {}\n    current_vertex_index = 0\n    while not poly_iter.isDone():\n        poly_index = poly_iter.index()\n        # We iterate over the triangles, even for quad meshes because that is what \n        # we need for the GPU. We shouldn't expect the mesh to be triangulated.\n        num_tris = poly_iter.numTriangles()\n        for tri_id in range(num_tris):\n            # global vertex indices for the current triangle\n            a, b, c = mesh_fn.getPolygonTriangleVertices(poly_index, tri_id)\n            for global_idx in &#91;a, b, c&#93;:\n                local_vert_idx = None\n                for local_idx in range(poly_iter.polygonVertexCount()):\n                    if poly_iter.vertexIndex(local_idx) == global_idx:\n                        local_vert_idx = local_idx\n                        break\n                # Handle the no UVs case, doesn't appear to be a nicer way?\n                try:\n                    uv_id = mesh_fn.getPolygonUVid(poly_index, local_vert_idx)\n                except RuntimeError:\n                    uv_id = -1\n                normal_id = poly_iter.normalIndex(local_vert_idx)\n                vert_key = (global_idx, uv_id, normal_id)\n                if vert_key not in vertex_map:\n                    pt = point_array&#91;global_idx&#93;\n                    positions.extend(&#91;pt.x, pt.y, pt.z&#93;)\n                    normal = mesh_fn.getFaceVertexNormal(poly_index, global_idx)\n                    normals.extend(&#91;normal.x, normal.y, normal.z&#93;)\n                    if uv_id !=-1:\n                        u, v = mesh_fn.getPolygonUV(poly_index, local_vert_idx)\n                        uvs.extend(&#91;u, v&#93;)\n                    color = poly_iter.getColor(local_vert_idx)\n                    colors.extend(&#91;\n                        int(color.r * 255),\n                        int(color.g * 255),\n                        int(color.b * 255),\n                        int(color.a * 255)\n                    &#93;)\n                    vertex_map&#91;vert_key&#93; = current_vertex_index\n                    indices.append(current_vertex_index)\n                    current_vertex_index += 1\n                else:\n                    indices.append(vertex_map&#91;vert_key&#93;)\n        \n        poly_iter.next()\n    # If all the vertex colors are unset (all black) then we don't\n    # export them\n    if colors == &#91;0, 0, 0, 255&#93; * (len(positions) \/\/ 3):\n        colors = []\n\n    return Geometry(positions, normals, uvs, colors, indices)<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">import<\/span><span style=\"color: #D8DEE9FF\"> maya<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">api<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">OpenMaya <\/span><span style=\"color: #81A1C1\">as<\/span><span style=\"color: #D8DEE9FF\"> om<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">def<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">convert_to_geometry<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9\">object_path<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">str<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">-&gt;<\/span><span style=\"color: #D8DEE9FF\"> Geometry<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">&quot;&quot;&quot;<\/span><span style=\"color: #A3BE8C\"> Converts the given Maya object to Geometry for<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    export to StaticRectangle<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    <\/span><span style=\"color: #ECEFF4\">&quot;&quot;&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #616E88\"># Get the mesh<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    sel <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> om<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">MSelectionList<\/span><span style=\"color: #ECEFF4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    sel<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">add<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">object_path<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    dag_path <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> sel<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getDagPath<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    mesh_fn <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> om<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">MFnMesh<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">dag_path<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    poly_iter <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> om<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">MItMeshPolygon<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">dag_path<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #616E88\"># getPoints returns an MPointArray of deduped vertices<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    point_array <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> mesh_fn<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getPoints<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">om<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">MSpace<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">kObject<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    positions <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">[]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    normals <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">[]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    uvs <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">[]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    colors <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">[]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    indices <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">[]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #616E88\"># vertex_map&#91;(global_idx , uv_id , normal_id )&#93; = expanded vertex index<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    vertex_map <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    current_vertex_index <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">while<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">not<\/span><span style=\"color: #D8DEE9FF\"> poly_iter<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">isDone<\/span><span style=\"color: #ECEFF4\">():<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        poly_index <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> poly_iter<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">index<\/span><span style=\"color: #ECEFF4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #616E88\"># We iterate over the triangles, even for quad meshes because that is what <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #616E88\"># we need for the GPU. We shouldn&#39;t expect the mesh to be triangulated.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        num_tris <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> poly_iter<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">numTriangles<\/span><span style=\"color: #ECEFF4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">for<\/span><span style=\"color: #D8DEE9FF\"> tri_id <\/span><span style=\"color: #81A1C1\">in<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">range<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">num_tris<\/span><span style=\"color: #ECEFF4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #616E88\"># global vertex indices for the current triangle<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            a<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> b<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> c <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> mesh_fn<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getPolygonTriangleVertices<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">poly_index<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> tri_id<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #81A1C1\">for<\/span><span style=\"color: #D8DEE9FF\"> global_idx <\/span><span style=\"color: #81A1C1\">in<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #D8DEE9FF\">a<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> b<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> c<\/span><span style=\"color: #ECEFF4\">&#93;:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                local_vert_idx <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">None<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #81A1C1\">for<\/span><span style=\"color: #D8DEE9FF\"> local_idx <\/span><span style=\"color: #81A1C1\">in<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">range<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">poly_iter<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">polygonVertexCount<\/span><span style=\"color: #ECEFF4\">()):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> poly_iter<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">vertexIndex<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">local_idx<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">==<\/span><span style=\"color: #D8DEE9FF\"> global_idx<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                        local_vert_idx <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> local_idx<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                        <\/span><span style=\"color: #81A1C1\">break<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #616E88\"># Handle the no UVs case, doesn&#39;t appear to be a nicer way?<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #81A1C1\">try<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    uv_id <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> mesh_fn<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getPolygonUVid<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">poly_index<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> local_vert_idx<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #81A1C1\">except<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">RuntimeError<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    uv_id <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #B48EAD\">1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                normal_id <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> poly_iter<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">normalIndex<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">local_vert_idx<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                vert_key <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">global_idx<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> uv_id<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> normal_id<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> vert_key <\/span><span style=\"color: #81A1C1\">not<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">in<\/span><span style=\"color: #D8DEE9FF\"> vertex_map<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    pt <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> point_array<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #D8DEE9FF\">global_idx<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    positions<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">extend<\/span><span style=\"color: #ECEFF4\">(&#91;<\/span><span style=\"color: #D8DEE9FF\">pt<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">x<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> pt<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">y<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> pt<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">z<\/span><span style=\"color: #ECEFF4\">&#93;)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    normal <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> mesh_fn<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getFaceVertexNormal<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">poly_index<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> global_idx<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    normals<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">extend<\/span><span style=\"color: #ECEFF4\">(&#91;<\/span><span style=\"color: #D8DEE9FF\">normal<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">x<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> normal<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">y<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> normal<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">z<\/span><span style=\"color: #ECEFF4\">&#93;)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> uv_id <\/span><span style=\"color: #81A1C1\">!=-<\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                        u<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> v <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> mesh_fn<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getPolygonUV<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">poly_index<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> local_vert_idx<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                        uvs<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">extend<\/span><span style=\"color: #ECEFF4\">(&#91;<\/span><span style=\"color: #D8DEE9FF\">u<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> v<\/span><span style=\"color: #ECEFF4\">&#93;)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    color <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> poly_iter<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getColor<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">local_vert_idx<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    colors<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">extend<\/span><span style=\"color: #ECEFF4\">(&#91;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                        <\/span><span style=\"color: #88C0D0\">int<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">color<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">r <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                        <\/span><span style=\"color: #88C0D0\">int<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">color<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">g <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                        <\/span><span style=\"color: #88C0D0\">int<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">color<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">b <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                        <\/span><span style=\"color: #88C0D0\">int<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">color<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">a <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    <\/span><span style=\"color: #ECEFF4\">&#93;)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    vertex_map<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #D8DEE9FF\">vert_key<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> current_vertex_index<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    indices<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">append<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">current_vertex_index<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    current_vertex_index <\/span><span style=\"color: #81A1C1\">+=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #81A1C1\">else<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    indices<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">append<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">vertex_map<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #D8DEE9FF\">vert_key<\/span><span style=\"color: #ECEFF4\">&#93;)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        poly_iter<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">next<\/span><span style=\"color: #ECEFF4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #616E88\"># If all the vertex colors are unset (all black) then we don&#39;t<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #616E88\"># export them<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> colors <\/span><span style=\"color: #81A1C1\">==<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">255<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">positions<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">\/\/<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">3<\/span><span style=\"color: #ECEFF4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        colors <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">[]<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Geometry<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">positions<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> normals<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> uvs<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> colors<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> indices<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>I say this part is just Maya implementation detail, because you could do the exact same thing in any other software (that supports python scripting) and provided you can spit out the same <code>Geometry<\/code> dataclass, then you can use the next snippet in any software.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Exporting the Geometry<\/h2>\n\n\n\n<p>This next part is actually pretty simple, at least in comparison to the preparation stage. I&#8217;m using <code>gzip<\/code> to compress a <code>bytearray<\/code> that I&#8217;m constructing using <code>struct<\/code> to pack the various arrays of the <code>Geometry<\/code>. <\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:1rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.5rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>import gzip\nimport struct\n\ndef export_geometry(geometry: Geometry, filename: str) -> None:\n    \"\"\" Write geometry data to a binary file\n\n    Format: All data is little-endian\n    - Header: 5 uint32s (vertex_count, index_count, index_format, has_uvs, has_colors)\n    - Positions: float32 array\n    - Normals: float32 array (if has_normals)\n    - UVs: float32 array (if has_uvs)\n    - Colors: uint8 array (if has_colors)\n    - Indices: uint16\/32 array\n\n    # TODO: Handle optional indexing\n    \"\"\"\n    vertex_count = len(geometry.positions) \/\/ 3\n    index_count = len(geometry.indices)\n    has_uvs = 1 if len(geometry.uvs) else 0\n    has_colors = 1 if len(geometry.colors) else 0\n    index_format = 16 if index_count &lt; 65535 else 32\n    data = bytearray()\n    data.extend(struct.pack('&lt;5I', vertex_count, index_count, index_format, has_uvs, has_colors))\n    data.extend(struct.pack(f'&lt;{len(geometry.positions)}f', *geometry.positions))\n    data.extend(struct.pack(f'&lt;{len(geometry.normals)}f', *geometry.normals))\n\n    if has_uvs:\n        data.extend(struct.pack(f'&lt;{len(geometry.uvs)}f', *geometry.uvs))\n\n    if has_colors:\n        data.extend(struct.pack(f'&lt;{len(geometry.colors)}B', *geometry.colors))\n\n    dtype = 'H' if index_format == 16 else 'I'\n    data.extend(struct.pack(f'&lt;{len(geometry.indices)}{dtype}', *geometry.indices))\n\n    with gzip.open(filename, 'wb', compresslevel=9) as handle:\n        handle.write(data)<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">import<\/span><span style=\"color: #D8DEE9FF\"> gzip<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">import<\/span><span style=\"color: #D8DEE9FF\"> struct<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">def<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">export_geometry<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9\">geometry<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> Geometry<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">filename<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">str<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">-&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">None<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">&quot;&quot;&quot;<\/span><span style=\"color: #A3BE8C\"> Write geometry data to a binary file<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    Format: All data is little-endian<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    - Header: 5 uint32s (vertex_count, index_count, index_format, has_uvs, has_colors)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    - Positions: float32 array<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    - Normals: float32 array (if has_normals)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    - UVs: float32 array (if has_uvs)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    - Colors: uint8 array (if has_colors)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    - Indices: uint16\/32 array<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    # <\/span><span style=\"color: #81A1C1\">TODO<\/span><span style=\"color: #A3BE8C\">: Handle optional indexing<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">    <\/span><span style=\"color: #ECEFF4\">&quot;&quot;&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    vertex_count <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">positions<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">\/\/<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">3<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    index_count <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">indices<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    has_uvs <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">uvs<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">else<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    has_colors <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">colors<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">else<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    index_format <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">16<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> index_count <\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">65535<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">else<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">32<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    data <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">bytearray<\/span><span style=\"color: #ECEFF4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    data<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">extend<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">struct<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">pack<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">&lt;5I<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> vertex_count<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> index_count<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> index_format<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> has_uvs<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> has_colors<\/span><span style=\"color: #ECEFF4\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    data<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">extend<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">struct<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">pack<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">f<\/span><span style=\"color: #A3BE8C\">&#39;&lt;<\/span><span style=\"color: #EBCB8B\">{<\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">positions<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #EBCB8B\">}<\/span><span style=\"color: #A3BE8C\">f&#39;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">positions<\/span><span style=\"color: #ECEFF4\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    data<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">extend<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">struct<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">pack<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">f<\/span><span style=\"color: #A3BE8C\">&#39;&lt;<\/span><span style=\"color: #EBCB8B\">{<\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">normals<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #EBCB8B\">}<\/span><span style=\"color: #A3BE8C\">f&#39;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">normals<\/span><span style=\"color: #ECEFF4\">))<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> has_uvs<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        data<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">extend<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">struct<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">pack<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">f<\/span><span style=\"color: #A3BE8C\">&#39;&lt;<\/span><span style=\"color: #EBCB8B\">{<\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">uvs<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #EBCB8B\">}<\/span><span style=\"color: #A3BE8C\">f&#39;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">uvs<\/span><span style=\"color: #ECEFF4\">))<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> has_colors<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        data<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">extend<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">struct<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">pack<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">f<\/span><span style=\"color: #A3BE8C\">&#39;&lt;<\/span><span style=\"color: #EBCB8B\">{<\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">colors<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #EBCB8B\">}<\/span><span style=\"color: #A3BE8C\">B&#39;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">colors<\/span><span style=\"color: #ECEFF4\">))<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    dtype <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">H<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> index_format <\/span><span style=\"color: #81A1C1\">==<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">16<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">else<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">I<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    data<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">extend<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">struct<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">pack<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">f<\/span><span style=\"color: #A3BE8C\">&#39;&lt;<\/span><span style=\"color: #EBCB8B\">{<\/span><span style=\"color: #88C0D0\">len<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">indices<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #EBCB8B\">}{<\/span><span style=\"color: #D8DEE9FF\">dtype<\/span><span style=\"color: #EBCB8B\">}<\/span><span style=\"color: #A3BE8C\">&#39;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\">geometry<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">indices<\/span><span style=\"color: #ECEFF4\">))<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">with<\/span><span style=\"color: #D8DEE9FF\"> gzip<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">open<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">filename<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">wb<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">compresslevel<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #B48EAD\">9<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">as<\/span><span style=\"color: #D8DEE9FF\"> handle<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        handle<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">write<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">data<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The first thing I do is write some data as a header for the file. This is necessary so we know <em>how<\/em> to read the data back in on the other side. We refer to this as a specification, as we are specifying how the bytes are laid out. If this were a proper specification, it would likely include other metadata, like version info and other bits and pieces. But for now, I&#8217;m keeping it simple.<\/p>\n\n\n\n<p>A few things to point out here, <code>index_format<\/code> I&#8217;m storing this in the header, so I know what TypedArray to create when reading it in. For anything less than <strong>65535 <\/strong>indices, we&#8217;ll use a <code>Uint16Array<\/code>, this will save some space. For anything else, we use a Uint32Array which will allow us to store up to <strong>4,294,967,295<\/strong> indices.<\/p>\n\n\n\n<p>If you&#8217;re not overly familiar with python syntax, the <code>struct.pack<\/code> calls takes the endianness, datatype and how many to allocate as the first string, then the data to pack as a flat list. <\/p>\n\n\n\n<p>Breaking down the header is the simplest:<\/p>\n\n\n\n<p>The string <code>'&lt;5I'<\/code><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>&#8216;<code>&lt;<\/code>&#8216;: little endian<\/li>\n\n\n\n<li>&#8216;<code>5<\/code>&#8216;: 5 elements<\/li>\n\n\n\n<li>&#8216;<code>I<\/code>&#8216;: 32bit integer<\/li>\n<\/ul>\n\n\n\n<p>then packing the <code>vertex_count<\/code>, <code>index_count<\/code>, <code>index_forma<\/code>t, <code>has_uvs (1\/0)<\/code> and <code>has_colors (1\/0)<\/code> into those bytes. If you&#8217;re wondering, a 32bit integer is 4bytes, so the header is 4bytes * 5 elements = 20bytes.<\/p>\n\n\n\n<p>The other lines look more complex, the string formatting is adding an extra complication, but it&#8217;s doing the same thing:<\/p>\n\n\n\n<p>The string <code>f'&lt;{len(geometry.positions)}f'<\/code><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>f<\/code>: the initial <code>f<\/code> tells python to do f-string formatting, substituting in place at runtime. <\/li>\n\n\n\n<li>&#8216;&lt;&#8216;: little endian<\/li>\n\n\n\n<li>&#8216;<code>{len(geometry.positions)}<\/code>: when evaluated results in the number of positions, e.g &#8216;<code>72<\/code>&#8216;<\/li>\n\n\n\n<li>&#8216;<code>f<\/code>&#8216;: 32bit float<\/li>\n<\/ul>\n\n\n\n<p>Using our cube as an example, this would result in the string &#8216;<code>&lt;72f'<\/code> which means we&#8217;re going to pack 72 elements into those bytes in the byte array.<\/p>\n\n\n\n<p>The next argument <code>*geometry.positions<\/code> is some syntactic sugar that tells python to unpack the geometry.positions array in place, so it would be as if we have manually done this:<\/p>\n\n\n\n<p><code>struct.pack('&lt;72f', -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, 0.5....)<\/code><\/p>\n\n\n\n<p>The very last line is where I use <code>gzip<\/code> to write out the byte array to disk, compressing it at the same time.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Importing the Geometry<\/h2>\n\n\n\n<p>Getting the geometry out is great, but how about getting it back in again? Would you believe this is actually quite simple. Because I have written this data out precisely how the renderer expects it, then all we need to do is uncompress then unpack our byte arrays using the header data to tell us how many bytes to read into each.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:1rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.5rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\/**\n * Parse StaticRectangle binary geometry file.\n * Format:\n * \n * Header (20 bytes):\n *   - uint32: vertex count\n *   - uint32: index count\n *   - uint32: index format (16 or 32)\n *   - uint32: has UVs\n *   - uint32: has colors\n *\n * Vertex Positions (vertexCount * 3 * float32)\n * Vertex Normals (vertexCount * 3 * float32)\n * Vertex UVs (if has UVs) (vertexCount * 2 * float32)\n * Vertex Colors (if has colors) (vertexCount * 4 * uint8)\n * Indices (indexCount * uint16 or uint32)\n *\/\nfunction parseGeometryBuffer(buffer: ArrayBuffer): Geometry {\n\n    const header = new DataView(buffer, 0, 20);\n    const vertexCount = header .getUint32(0, true);\n    const indexCount = header .getUint32(4, true);\n    const indexFormat = header .getUint32(8, true);\n    const hasUVs = header .getUint32(12, true) === 1;\n    const hasColors = header .getUint32(16, true) === 1;\n\n    let offset = 20;\n\n    const positions = new Float32Array(buffer, offset, vertexCount * 3);\n    offset += vertexCount * 3 * 4;\n\n    let normals: Float32Array | null = null;\n    normals = new Float32Array(buffer, offset, vertexCount * 3);\n    offset += vertexCount * 3 * 4;\n\n    let uvs: Float32Array | undefined = undefined;\n    if (hasUVs) {\n        uvs = new Float32Array(buffer, offset, vertexCount * 2);\n        offset += vertexCount * 2 * 4;\n    }\n\n    let colors: Uint8Array | undefined = undefined;\n    if (hasColors) {\n        colors = new Uint8Array(buffer, offset, vertexCount * 4);\n        offset += vertexCount * 4;\n    }\n\n    let indices: Uint16Array | Uint32Array;\n    if (indexFormat == 16) {\n        indices = new Uint16Array(buffer, offset, indexCount);\n    } else {\n        indices = new Uint32Array(buffer, offset, indexCount);\n    }\n\n    return {\n        positions,\n        uvs,\n        normals,\n        colors,\n        indices\n    };\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">\/**<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> Parse StaticRectangle binary geometry file<\/span><span style=\"color: #ECEFF4\">.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> Format<\/span><span style=\"color: #ECEFF4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Header<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #B48EAD\">20<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">bytes<\/span><span style=\"color: #ECEFF4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\"> uint32<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> vertex count<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\"> uint32<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> index count<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\"> uint32<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> index <\/span><span style=\"color: #88C0D0\">format<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #B48EAD\">16<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">or<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">32<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\"> uint32<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> has UVs<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\"> uint32<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> has colors<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> Vertex <\/span><span style=\"color: #88C0D0\">Positions<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">3<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> float32<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> Vertex <\/span><span style=\"color: #88C0D0\">Normals<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">3<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> float32<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> Vertex <\/span><span style=\"color: #88C0D0\">UVs<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> has UVs<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">2<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> float32<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> Vertex <\/span><span style=\"color: #88C0D0\">Colors<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> has colors<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">4<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> uint8<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Indices<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">indexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> uint16 <\/span><span style=\"color: #81A1C1\">or<\/span><span style=\"color: #D8DEE9FF\"> uint32<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">function <\/span><span style=\"color: #88C0D0\">parseGeometryBuffer<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">buffer<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> ArrayBuffer<\/span><span style=\"color: #ECEFF4\">):<\/span><span style=\"color: #D8DEE9FF\"> Geometry <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    const header = new <\/span><span style=\"color: #88C0D0\">DataView<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">buffer<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">20<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    const vertexCount = header <\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getUint32<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> true<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    const indexCount = header <\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getUint32<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #B48EAD\">4<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> true<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    const indexFormat = header <\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getUint32<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #B48EAD\">8<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> true<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    const hasUVs = header <\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getUint32<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #B48EAD\">12<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> true<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">==<\/span><span style=\"color: #D8DEE9FF\">= <\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    const hasColors = header <\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">getUint32<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #B48EAD\">16<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> true<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">==<\/span><span style=\"color: #D8DEE9FF\">= <\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    let offset = <\/span><span style=\"color: #B48EAD\">20<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    const positions = new <\/span><span style=\"color: #88C0D0\">Float32Array<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">buffer<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> offset<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">3<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    offset <\/span><span style=\"color: #81A1C1\">+<\/span><span style=\"color: #D8DEE9FF\">= vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">3<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">4<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    let normals<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> Float32Array <\/span><span style=\"color: #81A1C1\">|<\/span><span style=\"color: #D8DEE9FF\"> null = null;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    normals = new <\/span><span style=\"color: #88C0D0\">Float32Array<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">buffer<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> offset<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">3<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    offset <\/span><span style=\"color: #81A1C1\">+<\/span><span style=\"color: #D8DEE9FF\">= vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">3<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">4<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    let uvs<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> Float32Array <\/span><span style=\"color: #81A1C1\">|<\/span><span style=\"color: #D8DEE9FF\"> undefined = undefined;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">hasUVs<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        uvs = new <\/span><span style=\"color: #88C0D0\">Float32Array<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">buffer<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> offset<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">2<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        offset <\/span><span style=\"color: #81A1C1\">+<\/span><span style=\"color: #D8DEE9FF\">= vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">2<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">4<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    let colors<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> Uint8Array <\/span><span style=\"color: #81A1C1\">|<\/span><span style=\"color: #D8DEE9FF\"> undefined = undefined;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">hasColors<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        colors = new <\/span><span style=\"color: #88C0D0\">Uint8Array<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">buffer<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> offset<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">4<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        offset <\/span><span style=\"color: #81A1C1\">+<\/span><span style=\"color: #D8DEE9FF\">= vertexCount <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">4<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    let indices<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> Uint16Array <\/span><span style=\"color: #81A1C1\">|<\/span><span style=\"color: #D8DEE9FF\"> Uint32Array;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">indexFormat <\/span><span style=\"color: #81A1C1\">==<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">16<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        indices = new <\/span><span style=\"color: #88C0D0\">Uint16Array<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">buffer<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> offset<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> indexCount<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">else<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        indices = new <\/span><span style=\"color: #88C0D0\">Uint32Array<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">buffer<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> offset<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> indexCount<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        positions<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        uvs<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        normals<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        colors<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        indices<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>This should be reasonably easy to follow, as it&#8217;s just the reverse of what we just went through in <code>python<\/code>, but in <code>TypeScript<\/code>.<\/p>\n\n\n\n<p>You may notice I have hardcoded the number\/sizes of bytes, this is one benefit of a specification you&#8217;re allowed to make assumptions. It&#8217;s the reason many formats will store version information before a header, so you first read in the version and then you can adjust any parser logic on the fly, including changes to the file header.<\/p>\n\n\n\n<p>Lastly, we need something to call this parsing logic, and this is where the file decompression happens.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:1rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.5rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>export async function loadGeometry(url: string): Promise&lt;Geometry> {\n\n    const response = await fetch(url);\n\n    if (!response.ok) {\n        throw new Error(`Failed to load geometry: ${response.statusText}`);\n    }\n\n    let buffer: ArrayBuffer;\n\n    if (typeof DecompressionStream === 'undefined') {\n        log('DecompressionStream API not available, cannot load compressed geometry.', LogLevel.CRITICAL);\n        \/\/ For now throw, unitl we can show the fail card.\n        throw new Error('DecompressionStream API not available in this browser');\n    }\n\n    \/\/ Decompress using DecompressionStream API\n    const stream = response.body!.pipeThrough(\n        new DecompressionStream('gzip')\n    );\n    const decompressedResponse = new Response(stream);\n    buffer = await decompressedResponse.arrayBuffer();\n\n    return parseGeometryBuffer(buffer);\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9FF\">export <\/span><span style=\"color: #81A1C1\">async<\/span><span style=\"color: #D8DEE9FF\"> function <\/span><span style=\"color: #88C0D0\">loadGeometry<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">url<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> string<\/span><span style=\"color: #ECEFF4\">):<\/span><span style=\"color: #D8DEE9FF\"> Promise<\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9FF\">Geometry<\/span><span style=\"color: #81A1C1\">&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    const response = <\/span><span style=\"color: #81A1C1\">await<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">fetch<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">url<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9\">!<\/span><span style=\"color: #D8DEE9FF\">response<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">ok<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        throw new <\/span><span style=\"color: #88C0D0\">Error<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9\">`Failed to load geometry<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9\"> <\/span><span style=\"color: #D8DEE9\">$<\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #D8DEE9\">response<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">statusText<\/span><span style=\"color: #ECEFF4\">}<\/span><span style=\"color: #D8DEE9\">`<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    let buffer<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> ArrayBuffer;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">typeof DecompressionStream <\/span><span style=\"color: #81A1C1\">==<\/span><span style=\"color: #D8DEE9FF\">= <\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">undefined<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #88C0D0\">log<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">DecompressionStream API not available, cannot load compressed geometry.<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> LogLevel<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">CRITICAL<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">\/\/<\/span><span style=\"color: #D8DEE9FF\"> For now throw<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> unitl we can show the fail card<\/span><span style=\"color: #ECEFF4\">.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        throw new <\/span><span style=\"color: #88C0D0\">Error<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">DecompressionStream API not available in this browser<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">\/\/<\/span><span style=\"color: #D8DEE9FF\"> Decompress using DecompressionStream API<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    const stream = response<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">body!<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">pipeThrough<\/span><span style=\"color: #ECEFF4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        new <\/span><span style=\"color: #88C0D0\">DecompressionStream<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">gzip<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    const decompressedResponse = new <\/span><span style=\"color: #88C0D0\">Response<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">stream<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    buffer = <\/span><span style=\"color: #81A1C1\">await<\/span><span style=\"color: #D8DEE9FF\"> decompressedResponse<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">arrayBuffer<\/span><span style=\"color: #ECEFF4\">()<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">parseGeometryBuffer<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">buffer<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>And that&#8217;s it! instead of calling scene.<code>createShape(name, ExampleCube())<\/code> I can now call <code>scene.createShape(name, await loadGeometry('\/resources\/models\/cube.bin'))<\/code><\/p>\n\n\n\n<p>and hey presto, cube!<\/p>\n\n\n\n<figure class=\"wp-block-image size-medium\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"284\" src=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/exported_cube-300x284.png\" alt=\"\" class=\"wp-image-200\" srcset=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/exported_cube-300x284.png 300w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/exported_cube-1024x969.png 1024w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/exported_cube-768x727.png 768w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/exported_cube-250x237.png 250w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/exported_cube-550x520.png 550w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/exported_cube-800x757.png 800w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/exported_cube-190x180.png 190w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/exported_cube-317x300.png 317w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/exported_cube-528x500.png 528w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/exported_cube.png 1226w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><figcaption class=\"wp-element-caption\">The orientation difference here between Maya\/StaticRectangle is because the I moved the camera.<\/figcaption><\/figure>\n\n\n\n<p>Ok, So long, Cube.<\/p>\n\n\n\n<p>How about a nice axis model that can show us which way things are oriented.<\/p>\n\n\n\n<figure class=\"wp-block-image size-medium\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"290\" src=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/axis-300x290.png\" alt=\"\" class=\"wp-image-199\" srcset=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/axis-300x290.png 300w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/axis-250x242.png 250w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/axis-550x532.png 550w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/axis-186x180.png 186w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/axis-310x300.png 310w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/axis-516x500.png 516w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/axis.png 752w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/figure>\n\n\n\n<p>How about something with a few more vertices?<\/p>\n\n\n\n<figure class=\"wp-block-image size-medium\"><img loading=\"lazy\" decoding=\"async\" width=\"295\" height=\"300\" src=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky-295x300.png\" alt=\"\" class=\"wp-image-198\" srcset=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky-295x300.png 295w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky-1006x1024.png 1006w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky-768x781.png 768w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky-48x48.png 48w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky-250x254.png 250w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky-550x560.png 550w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky-800x814.png 800w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky-177x180.png 177w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky-491x500.png 491w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky.png 1198w\" sizes=\"(max-width: 295px) 100vw, 295px\" \/><\/figure>\n\n\n\n<p>Err, what is that?<\/p>\n\n\n\n<p>Unfortunately, without lighting or textures it&#8217;s kind of hard to tell what that is. I can actually make a very small tweak to the pipeline and change that topology from a <code>triangle-list<\/code> to <code>line-list<\/code> and instead of rendering triangles, we render edges (lines).<\/p>\n\n\n\n<figure class=\"wp-block-image size-medium\"><img loading=\"lazy\" decoding=\"async\" width=\"295\" height=\"300\" src=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky_wireframe-295x300.png\" alt=\"\" class=\"wp-image-197\" srcset=\"https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky_wireframe-295x300.png 295w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky_wireframe-1006x1024.png 1006w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky_wireframe-768x781.png 768w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky_wireframe-48x48.png 48w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky_wireframe-250x254.png 250w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky_wireframe-550x560.png 550w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky_wireframe-800x814.png 800w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky_wireframe-177x180.png 177w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky_wireframe-491x500.png 491w, https:\/\/leonwoud.com\/devblog\/wp-content\/uploads\/2025\/10\/spooky_wireframe.png 1198w\" sizes=\"(max-width: 295px) 100vw, 295px\" \/><figcaption class=\"wp-element-caption\">Spooky!<\/figcaption><\/figure>\n\n\n\n<p>Now I&#8217;m getting custom geometry into StaticRectangle that supports all the <code>Geometry<\/code> features like UVs for textures and normals for lighting, that is where we&#8217;re heading next!<\/p>\n\n\n<ol class=\"wp-block-footnotes has-small-font-size\"><li id=\"4f8a7e3b-6d77-43b2-b663-60d393b35b20\">I&#8217;ve read many posts\/articles on this topic, some &#8216;elitists&#8217; think game developers have gotten lazy when it comes to optimisations for storage vs memory etc. but I think that is probably a bit of a generalisation. You can always make things better, but I do understand that it comes down to cost vs reward. <a href=\"#4f8a7e3b-6d77-43b2-b663-60d393b35b20-link\" aria-label=\"Jump to footnote reference 1\">\u21a9\ufe0e<\/a><\/li><li id=\"3e153b78-f939-4e9c-a45d-e823b1c69c6f\">You may (probably not) notice that my code snippets don&#8217;t use the NZ\/UK English spelling of words, but I do in prose. This is purely habitual and because I have worked with a lot of American code in my career. If I see colour written as color outside of code it just looks wrong, and the same thing goes vice versa! stange&#8230; <a href=\"#3e153b78-f939-4e9c-a45d-e823b1c69c6f-link\" aria-label=\"Jump to footnote reference 2\">\u21a9\ufe0e<\/a><\/li><\/ol>\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s time to look into getting different geometry into our scene. In the Vulkan version of StaticRectangle, I was using obj files for meshes and a 3rd party library called tinyobjloader to load them. This was fine for that application because the size of the files wasn&#8217;t much of a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"bgseo_title":"","bgseo_description":"","bgseo_robots_index":"index","bgseo_robots_follow":"follow","footnotes":"[{\"content\":\"I've read many posts\/articles on this topic, some 'elitists' think game developers have gotten lazy when it comes to optimisations for storage vs memory etc. but I think that is probably a bit of a generalisation. You can always make things better, but I do understand that it comes down to cost vs reward.\",\"id\":\"4f8a7e3b-6d77-43b2-b663-60d393b35b20\"},{\"content\":\"You may (probably not) notice that my code snippets don't use the NZ\/UK English spelling of words, but I do in prose. This is purely habitual and because I have worked with a lot of American code in my career. If I see colour written as color outside of code it just looks wrong, and the same thing goes vice versa! stange...\",\"id\":\"3e153b78-f939-4e9c-a45d-e823b1c69c6f\"}]"},"categories":[9,23],"tags":[12,22],"class_list":["post-187","post","type-post","status-publish","format-standard","hentry","category-game-dev","category-webgpu","tag-renderer","tag-webgpu"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/leonwoud.com\/devblog\/wp-json\/wp\/v2\/posts\/187","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/leonwoud.com\/devblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/leonwoud.com\/devblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/leonwoud.com\/devblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/leonwoud.com\/devblog\/wp-json\/wp\/v2\/comments?post=187"}],"version-history":[{"count":5,"href":"https:\/\/leonwoud.com\/devblog\/wp-json\/wp\/v2\/posts\/187\/revisions"}],"predecessor-version":[{"id":204,"href":"https:\/\/leonwoud.com\/devblog\/wp-json\/wp\/v2\/posts\/187\/revisions\/204"}],"wp:attachment":[{"href":"https:\/\/leonwoud.com\/devblog\/wp-json\/wp\/v2\/media?parent=187"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/leonwoud.com\/devblog\/wp-json\/wp\/v2\/categories?post=187"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/leonwoud.com\/devblog\/wp-json\/wp\/v2\/tags?post=187"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}