<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>nt3d</title><link>https://nimbleterminal.github.io/nt3d/docs/</link><description>Recent content on nt3d</description><generator>Hugo</generator><language>en</language><atom:link href="https://nimbleterminal.github.io/nt3d/docs/index.xml" rel="self" type="application/rss+xml"/><item><title>Getting started</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/getting-started/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/getting-started/</guid><description>&lt;h1 id="getting-started"&gt;Getting started&lt;a class="anchor" href="#getting-started"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;h2 id="install"&gt;Install&lt;a class="anchor" href="#install"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;nt3d is a Go module, &lt;code&gt;nimbleterminal.dev/nt3d&lt;/code&gt;, and needs Go 1.26.8 or later:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go get nimbleterminal.dev/nt3d@latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="the-packages"&gt;The packages&lt;a class="anchor" href="#the-packages"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;You import only what you use. The core package reads no files; &lt;code&gt;nt3dmesh&lt;/code&gt; reads the common formats, and the others register themselves when imported, so a program that reads no glTF carries no glTF.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Import&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Package&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;What it is for&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;nimbleterminal.dev/nt3d&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;nt3d&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Scenes, cameras, geometry, textures, picking, and rendering&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;nimbleterminal.dev/nt3d/math&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;nt3dmath&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Float32 vectors, matrices, rays, and bounds&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;nimbleterminal.dev/nt3d/widget&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;nt3dwidget&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The Bubble Tea widget, with Kitty graphics or colored glyphs&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;nimbleterminal.dev/nt3d/mesh&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;nt3dmesh&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Reading and writing STL, OBJ, 3MF, and PLY&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;nimbleterminal.dev/nt3d/mesh/gltf&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;nt3dgltf&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Reading glTF 2.0 (&lt;code&gt;.gltf&lt;/code&gt; and &lt;code&gt;.glb&lt;/code&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;nimbleterminal.dev/nt3d/mesh/formats&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;formats&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Imported for its side effects: every optional mesh format, and WebP textures&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="a-first-program"&gt;A first program&lt;a class="anchor" href="#a-first-program"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;This shows an orange ball in the terminal. Drag to orbit it, scroll to zoom, and press &lt;code&gt;q&lt;/code&gt; to quit.&lt;/p&gt;</description></item><item><title>Scenes</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/scenes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/scenes/</guid><description>&lt;h1 id="scenes"&gt;Scenes&lt;a class="anchor" href="#scenes"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;A &lt;code&gt;Scene&lt;/code&gt; is a tree of nodes. Each node has a name, a place in the tree, a transform relative to its parent, a visibility switch, and optionally some geometry to draw. Moving, hiding, or rotating a node does the same to everything under it.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewScene&lt;/span&gt;()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;group&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Add&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Root&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;objects&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; { panic(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;) }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;SetGeometry&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;marker&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Geometry&lt;/span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Points&lt;/span&gt;: []&lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Point&lt;/span&gt;{{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Position&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;nt3dmath&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Vec3&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;Z&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;.5&lt;/span&gt;},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Radius&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Color&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;color&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;RGBA&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;R&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;240&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;A&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; { panic(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;) }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Reparent&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;group&lt;/span&gt;); &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; { panic(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;) }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;SetTransform&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;group&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;nt3dmath&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;RotationZ&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;.4&lt;/span&gt;)); &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; { panic(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;) }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="nodes"&gt;Nodes&lt;a class="anchor" href="#nodes"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;Root&lt;/code&gt; is always there, and every other node descends from it.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Add(parent, name)&lt;/code&gt; makes an empty group. Names must be unique and not empty.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;SetGeometry(name, g)&lt;/code&gt; creates a node with that geometry, or replaces the geometry of the node already called that. &lt;code&gt;SetNodeGeometry(id, g)&lt;/code&gt; does the same by ID.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Reparent&lt;/code&gt; moves a node under a new parent, keeping its local transform. A node can&amp;rsquo;t become its own ancestor.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;SetVisible&lt;/code&gt; hides or shows a node and everything under it.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Remove&lt;/code&gt; deletes a node and everything under it.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Node(id)&lt;/code&gt; returns a copy of a node&amp;rsquo;s details.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Node IDs are stable and never reused, so an ID you hold either still means the same node or means nothing.&lt;/p&gt;</description></item><item><title>Shapes and geometry</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/shapes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/shapes/</guid><description>&lt;h1 id="shapes-and-geometry"&gt;Shapes and geometry&lt;a class="anchor" href="#shapes-and-geometry"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;A &lt;code&gt;Geometry&lt;/code&gt; is what a node draws: triangles (with colors, normals, and texture coordinates), points, boxes, lines, and arrows, plus a material. You can fill one in by hand, but usually you build one or &lt;a href="../meshes/"&gt;read one from a file&lt;/a&gt;.&lt;/p&gt;&#10;&lt;img class="poster" src="../../posters/shapes.png" alt="A textured cube, golden torus, purple cone, orange cylinder, and green globe in a row"&gt;&#10;&lt;h2 id="building-shapes"&gt;Building shapes&lt;a class="anchor" href="#building-shapes"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;BuildSphere&lt;/code&gt;, &lt;code&gt;BuildCylinder&lt;/code&gt;, &lt;code&gt;BuildTorus&lt;/code&gt;, and &lt;code&gt;BuildCuboid&lt;/code&gt; each take a small spec struct and return a lit, UV-mapped mesh centered on the origin, with Z up. Leave the tessellation counts and color at zero for sensible defaults.&lt;/p&gt;</description></item><item><title>Materials and textures</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/materials/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/materials/</guid><description>&lt;h1 id="materials-and-textures"&gt;Materials and textures&lt;a class="anchor" href="#materials-and-textures"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;A geometry&amp;rsquo;s &lt;code&gt;Material&lt;/code&gt; says how its surface looks: its texture, whether light affects it, and whether it lets things show through. Without a material, a mesh is lit and colored by its vertex colors.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;tex&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewTexture&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;img&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// any image.Image&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;mat&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Material&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;Texture&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;tex&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Repeat&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Specular&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;.4&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="textures"&gt;Textures&lt;a class="anchor" href="#textures"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;NewTexture&lt;/code&gt; takes a snapshot of any &lt;code&gt;image.Image&lt;/code&gt;, up to 8192 pixels a side. Textures are sRGB, as images are, and both renderers filter them in linear light.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Repeat.&lt;/strong&gt; With &lt;code&gt;Material.Repeat&lt;/code&gt;, texture coordinates outside 0 to 1 wrap around, and filtering blends across the seams, so a texture made to tile shows none. Otherwise they clamp to the edge. The OBJ, PLY, and 3MF readers set it, since those formats tile by default.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Tint.&lt;/strong&gt; &lt;code&gt;Material.Tint&lt;/code&gt; multiplies the texture by the vertex colors.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Unlit.&lt;/strong&gt; &lt;code&gt;Material.Unlit&lt;/code&gt; shows colors as they are, ignoring the light.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="mipmaps"&gt;Mipmaps&lt;a class="anchor" href="#mipmaps"&gt;#&lt;/a&gt;&lt;/h3&gt;&#10;&lt;p&gt;A tiled floor running to the horizon packs many texels into each distant pixel, and without care it shimmers into moiré. nt3d keeps each texture with copies of itself halved down to one texel, averaged in linear light, and each pixel samples the copies that match how many texels it covers. &lt;code&gt;Frame.Anisotropy&lt;/code&gt; (1 to 16; zero means 16) goes further: where a pixel covers a long, thin run of texels, as on a floor seen at a slant, it samples along the run, so the floor keeps its detail instead of blurring to gray. The GPU and software renderers do the same.&lt;/p&gt;</description></item><item><title>Normal maps</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/normal-maps/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/normal-maps/</guid><description>&lt;p&gt;Normal maps add surface detail by changing the shading normal between vertices.&#10;GPU and software use them for diffuse light, specular highlights, and environment&#10;diffuse light. They leave silhouettes, bounds, picking, wireframe, and shadow-caster&#10;geometry unchanged. Shadow bias still uses the unperturbed surface normal.&lt;/p&gt;&#10;&lt;h2 id="materials"&gt;Materials&lt;a class="anchor" href="#materials"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;Decode a normal image with Go&amp;rsquo;s image packages, then create a linear-data texture:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;normal&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewNormalMap&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;img&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;mat&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Material&lt;/span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;NormalMap&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;normal&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;NormalStrength&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;NormalRepeat&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Specular&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0.4&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Shininess&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;48&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Assign the material to triangle geometry with one &lt;code&gt;Geometry.UV&lt;/code&gt; per vertex.&#10;&lt;code&gt;NormalStrength&lt;/code&gt; scales tangent-space X and Y. &lt;strong&gt;Set it to 1 for the authored&#10;strength; zero disables the normal perturbation.&lt;/strong&gt; Negative strengths reverse the&#10;X/Y slopes. &lt;code&gt;NormalRepeat&lt;/code&gt; controls wrapping independently of the base-color&#10;texture&amp;rsquo;s &lt;code&gt;Repeat&lt;/code&gt;. Both slots use the same &lt;code&gt;Geometry.UV&lt;/code&gt; coordinates.&lt;/p&gt;</description></item><item><title>Particles and sprites</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/particles/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/particles/</guid><description>&lt;h1 id="particles-and-sprites"&gt;Particles and sprites&lt;a class="anchor" href="#particles-and-sprites"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;&lt;a href="../demos/#particles"&gt;Try the particles demo&lt;/a&gt; with &lt;code&gt;task particles&lt;/code&gt;: &lt;code&gt;n&lt;/code&gt; cycles up to 100,000 particles, &lt;code&gt;t&lt;/code&gt; switches between discs and animated atlas sprites, &lt;code&gt;b&lt;/code&gt; changes blending, and &lt;code&gt;a&lt;/code&gt; pauses. All three renderers use the same scene; wireframe shows dots.&lt;/p&gt;&#10;&lt;h2 id="updating-points"&gt;Updating points&lt;a class="anchor" href="#updating-points"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;Create a geometry with &lt;code&gt;Points&lt;/code&gt; and &lt;code&gt;PointStyle&lt;/code&gt; as usual. For subsequent frames, keep its count and style and call:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;bounds&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3dmath&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;AABB&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;bounds&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Include&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;nt3dmath&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Vec3&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;X&lt;/span&gt;: &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Y&lt;/span&gt;: &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Z&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;})&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;bounds&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Include&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;nt3dmath&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Vec3&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;X&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Y&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Z&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;})&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;SetNodePoints&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;particles&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;bounds&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Or replace only a range, starting at index 100:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;UpdateNodePoints&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;particles&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;], &lt;span style="color:#a6e22e"&gt;bounds&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These methods check and copy the supplied points, share all unrelated geometry, and use the supplied bounds without recomputing them. Existing frame snapshots remain immutable, so each update still allocates a point slice; this API does not expose mutable scene storage. Partial updates copy that slice too, but validate and upload only the changed range. If the GPU skipped updates or replays an older frame, it uploads the complete point buffer to restore the correct snapshot. Buffer allocations, other primitive uploads, and repeated point validation during rendering are avoided. Alpha sorting is refreshed when points move.&lt;/p&gt;</description></item><item><title>Clustering and LOD</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/clustering/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/clustering/</guid><description>&lt;h1 id="clustering-and-lod"&gt;Clustering and LOD&lt;a class="anchor" href="#clustering-and-lod"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;&lt;a href="../demos/#point-cloud"&gt;Try the point cloud demo&lt;/a&gt; with &lt;code&gt;task cloud&lt;/code&gt;: a galaxy of up to a million points, &lt;code&gt;l&lt;/code&gt; switches clustering off to compare, &lt;code&gt;n&lt;/code&gt; cycles the count, and &lt;code&gt;-&lt;/code&gt; and &lt;code&gt;=&lt;/code&gt; zoom. All three renderers draw the same selection.&lt;/p&gt;&#10;&lt;p&gt;A large set of similar, independently placed items (points, instances, labels) need not be drawn whole every frame. &lt;code&gt;nimbleterminal.dev/nt3d/clod&lt;/code&gt; (&lt;code&gt;nt3dclod&lt;/code&gt;) groups such a set into spatial clusters once and, each frame, selects the clusters inside the view and a level of detail for each: near clusters list all their items, far ones a spread fraction or none, leaving you to draw one representative. The output is a compact list of item indices that the existing point, instance, or label paths draw. The package knows nothing of what an item is beyond its position, and never draws.&lt;/p&gt;</description></item><item><title>Spatial index</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/spatial-index/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/spatial-index/</guid><description>&lt;h1 id="spatial-index"&gt;Spatial index&lt;a class="anchor" href="#spatial-index"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;&lt;a href="../demos/#aabb-tree"&gt;Try the AABB tree demo&lt;/a&gt; with &lt;code&gt;task aabb&lt;/code&gt;: click a box to see its ancestors in the tree as nested wireframes, and set the boxes drifting to watch the tree update.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;nimbleterminal.dev/nt3d/aabbtree&lt;/code&gt; (&lt;code&gt;nt3daabbtree&lt;/code&gt;) is a dynamic AABB tree: a spatial index over a changing set of boxed objects that answers &amp;ldquo;which objects overlap this box?&amp;rdquo; and &amp;ldquo;which objects does this ray meet?&amp;rdquo; in logarithmic time. It is independent of scenes and renderers; you hand it bounds and a value of your own type, and it hands them back from queries. It is not a collision solver, and the static triangle hierarchy that &lt;a href="../picking/"&gt;picking&lt;/a&gt; uses stays as it is.&lt;/p&gt;</description></item><item><title>Fur</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/fur/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/fur/</guid><description>&lt;h1 id="fur"&gt;Fur&lt;a class="anchor" href="#fur"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;nt3d grows fur on any mesh with shells: copies of the surface pushed out a little further each time, each a cutout showing the cross-section of every strand at that height. Stacked, they read as strands.&lt;/p&gt;&#10;&lt;img class="poster" src="../../posters/fur.png" alt="Spot the cow covered in black-and-white fur"&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;coat&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewFur&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Fur&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;Length&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;.05&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Shells&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Spacing&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;.011&lt;/span&gt;})&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;shells&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;coat&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Shells&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;spot&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// textured, so the fur takes Spot&amp;#39;s colors&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;g&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;range&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;shells&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;SetGeometry&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sprintf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;fur %d&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;), &lt;span style="color:#a6e22e"&gt;g&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;&lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;SetTransform&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;coat&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sway&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;wind&lt;/span&gt;)) &lt;span style="color:#75715e"&gt;// each frame, to animate&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="the-coat"&gt;The coat&lt;a class="anchor" href="#the-coat"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;FurCoat.Shells(base)&lt;/code&gt; returns an opaque skin plus one cutout layer per shell. Each strand has its own place, length, and taper.&lt;/p&gt;</description></item><item><title>Mesh files</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/meshes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/meshes/</guid><description>&lt;h1 id="mesh-files"&gt;Mesh files&lt;a class="anchor" href="#mesh-files"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;&lt;code&gt;nt3dmesh&lt;/code&gt; reads and writes the common 3D file formats. Reading gives you a &lt;code&gt;Model&lt;/code&gt;, a list of objects, which you can add to a scene as one node each:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;model&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3dmesh&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ReadFile&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;part.obj&amp;#34;&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;nt3dmesh&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Options&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;Color&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;grey&lt;/span&gt;})&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;group&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;model&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;AddTo&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Root&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;part&amp;#34;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// one child node per object&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;fit&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;model&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Bounds&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;Normalization&lt;/span&gt;()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;SetTransform&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;group&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;fit&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// scale it to fit the default camera&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;img class="poster" src="../../posters/models.png" alt="A striped robot with a sky-blue head, orange arms, and a smiling screen"&gt;&#10;&lt;h2 id="formats"&gt;Formats&lt;a class="anchor" href="#formats"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Format&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Read&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Write&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;STL&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;binary and ASCII, with face colors&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;binary, with colors&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;OBJ&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;with materials, textures, and lines&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;triangles and lines&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3MF&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;with colors, textures, painting, and parts&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;one object, with face colors&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;PLY&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;ASCII and binary, including raw scans&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;binary&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;glTF 2.0&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;.gltf&lt;/code&gt; and &lt;code&gt;.glb&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;SOG&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;.sog&lt;/code&gt; bundles and &lt;code&gt;meta.json&lt;/code&gt; folders&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;STL, OBJ, 3MF, and PLY are built in. glTF is in its own package, &lt;code&gt;nt3d/mesh/gltf&lt;/code&gt;, &lt;a href="../rendering/#spz"&gt;SPZ Gaussian splats&lt;/a&gt; in &lt;code&gt;nt3d/mesh/spz&lt;/code&gt;, and &lt;a href="../rendering/#sog-and-compressed-ply"&gt;SOG Gaussian splats&lt;/a&gt; in &lt;code&gt;nt3d/mesh/sog&lt;/code&gt;; each registers itself when imported, the way image formats do with &lt;code&gt;image&lt;/code&gt;. To get every optional format, and WebP textures, import them all at once:&lt;/p&gt;</description></item><item><title>Animation and skinning</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/animation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/animation/</guid><description>&lt;p&gt;&lt;code&gt;AnimationClip&lt;/code&gt;, &lt;code&gt;Track&lt;/code&gt;, &lt;code&gt;Skeleton&lt;/code&gt;, and &lt;code&gt;Deformation&lt;/code&gt; are independent of file formats. Tracks target scene nodes; procedural code can supply the same data or update joint transforms and morph weights directly.&lt;/p&gt;&#10;&lt;h2 id="playing-and-sampling"&gt;Playing and sampling&lt;a class="anchor" href="#playing-and-sampling"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;clip&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;AnimationClip&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;Name&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;slide&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Tracks&lt;/span&gt;: []&lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Track&lt;/span&gt;{{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Node&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Channel&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Translation&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Width&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Times&lt;/span&gt;: []&lt;span style="color:#66d9ef"&gt;float32&lt;/span&gt;{&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Values&lt;/span&gt;: []&lt;span style="color:#66d9ef"&gt;float32&lt;/span&gt;{&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}}}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;player&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewAnimation&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;clip&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;player&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Mode&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;PingPong&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Once (clamped), Loop, or PingPong&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;player&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Step&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;elapsed&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Seconds&lt;/span&gt;()); &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;player&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Paused&lt;/span&gt; = &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;player&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sample&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;1.25&lt;/span&gt;); &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; } &lt;span style="color:#75715e"&gt;// works while paused&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Time is in seconds. &lt;code&gt;Speed&lt;/code&gt; defaults to one and may be negative. &lt;code&gt;Time&lt;/code&gt; is the unwrapped playback clock; &lt;code&gt;Sample&lt;/code&gt; folds it according to &lt;code&gt;Mode&lt;/code&gt;. &lt;code&gt;Once&lt;/code&gt; clamps at the endpoints. &lt;code&gt;Translation&lt;/code&gt; and &lt;code&gt;Scale&lt;/code&gt; use width three; &lt;code&gt;Rotation&lt;/code&gt; uses normalized XYZW quaternions and width four. &lt;code&gt;MorphWeights&lt;/code&gt; uses the target count. &lt;code&gt;UserChannel&lt;/code&gt; invokes the player&amp;rsquo;s &lt;code&gt;Apply(name, values)&lt;/code&gt; callback.&lt;/p&gt;</description></item><item><title>Environment and sky</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/environment/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/environment/</guid><description>&lt;p&gt;An environment gives a scene a distant sky and diffuse image-based lighting.&#10;It is optional: scenes without one keep their solid background and existing lighting.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;sky&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;GradientEnvironment&lt;/span&gt;(&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;color&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;RGBA&lt;/span&gt;{&lt;span style="color:#ae81ff"&gt;72&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;130&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;220&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;}, &lt;span style="color:#75715e"&gt;// zenith&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;color&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;RGBA&lt;/span&gt;{&lt;span style="color:#ae81ff"&gt;218&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;231&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;245&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;}, &lt;span style="color:#75715e"&gt;// horizon&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;color&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;RGBA&lt;/span&gt;{&lt;span style="color:#ae81ff"&gt;115&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;77&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;}, &lt;span style="color:#75715e"&gt;// ground&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;SetEnvironment&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;sky&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The widget picks it up through the scene&amp;rsquo;s frames. Call &lt;code&gt;view.Refresh()&lt;/code&gt; after&#10;changing it when the application is not already scheduling frames. Remove it&#10;with &lt;code&gt;scene.SetEnvironment(nil)&lt;/code&gt;. Environment objects are immutable and can be&#10;shared between scenes; frames retain the environment they were made with.&lt;/p&gt;</description></item><item><title>Labels and billboards</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/labels/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/labels/</guid><description>&lt;h1 id="labels-and-billboards"&gt;Labels and billboards&lt;a class="anchor" href="#labels-and-billboards"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;Labels are scene geometry. They move with their parents and use the same&#10;textured triangles, clipping, depth, and picking as other meshes on WebGPU&#10;and software. Wireframe uses depth-tested terminal text.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;SetLabel&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;peak&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Label&lt;/span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Text&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Peak\nz = 0.60&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Pixels&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Height&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;32&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Color&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;color&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;RGBA&lt;/span&gt;{&lt;span style="color:#ae81ff"&gt;245&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;248&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Background&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;color&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;RGBA&lt;/span&gt;{&lt;span style="color:#ae81ff"&gt;24&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;32&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;48&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Padding&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Border&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;BorderColor&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;color&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;RGBA&lt;/span&gt;{&lt;span style="color:#ae81ff"&gt;90&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;160&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;210&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Handle errors from each operation.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Reparent&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;terrainNode&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;SetTransform&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;nt3dmath&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Translation&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;nt3dmath&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Vec3&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;Z&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;.8&lt;/span&gt;}))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Height&lt;/code&gt; is the whole label&amp;rsquo;s height. With &lt;code&gt;Pixels&lt;/code&gt;, it stays the same size&#10;while zooming and supersampling; otherwise it is in world units. Zero height&#10;uses the texture&amp;rsquo;s native height in pixel mode, or one world unit. Labels are&#10;centered on the node origin. Camera-facing labels inherit parent translation;&#10;&lt;code&gt;Fixed: true&lt;/code&gt; keeps the local XY plane and inherits parent rotation and scale.&#10;Fixed opaque labels are single-sided, following the mesh winding convention.&#10;Blended quads draw both sides, as other transparent meshes do. Scene bounds&#10;include only the anchor for pixel-sized labels, so camera fitting does not&#10;interpret pixels as world units.&lt;/p&gt;</description></item><item><title>Wave surfaces</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/water/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/water/</guid><description>&lt;h1 id="wave-surfaces"&gt;Wave surfaces&lt;a class="anchor" href="#wave-surfaces"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;&lt;code&gt;WaveSurface&lt;/code&gt; combines directional Gerstner waves on the XY plane, with Z up&#10;and mean height zero. It supplies positions, analytic normals, tangents, and&#10;material-point velocities. One evaluator can move a water mesh and answer&#10;surface queries for floating objects. The surface is immutable and safe to&#10;sample concurrently; its zero value is flat water.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;surface&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewWaveSurface&lt;/span&gt;([]&lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;GerstnerWave&lt;/span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#a6e22e"&gt;Direction&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;nt3dmath&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Vec3&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;X&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Y&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;.3&lt;/span&gt;}, &lt;span style="color:#a6e22e"&gt;Wavelength&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;4.5&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Amplitude&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;.2&lt;/span&gt;},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#a6e22e"&gt;Direction&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;nt3dmath&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Vec3&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;X&lt;/span&gt;: &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;.3&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Y&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;}, &lt;span style="color:#a6e22e"&gt;Wavelength&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;2.7&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Amplitude&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;.09&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Phase&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1.1&lt;/span&gt;},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}, &lt;span style="color:#ae81ff"&gt;9.81&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Move a mesh vertex from its original, undisplaced XY coordinates.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;sample&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;surface&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sample&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;restX&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;restY&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;seconds&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;vertex&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Position&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;sample&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Position&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;vertex&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Normal&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;sample&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Normal&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Find the water under an object at a world XY location.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;water&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;surface&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;AtWorld&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;worldX&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;worldY&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;seconds&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;height&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;water&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Position&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Directions are normalized on construction and must have zero Z. Wavelengths&#10;are positive, amplitudes nonnegative, and phases are radians. With gravity&#10;9.81, lengths are meters and time is seconds. Other consistent units work too.&#10;Angular speed follows the deep-water relation &lt;code&gt;sqrt(gravity * 2π / wavelength)&lt;/code&gt;.&#10;The constructor rejects non-finite inputs and total steepness&#10;&lt;code&gt;sum(2π * amplitude / wavelength) &amp;gt;= 1&lt;/code&gt;, so waves cannot fold over themselves.&lt;/p&gt;</description></item><item><title>Rendering</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/rendering/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/rendering/</guid><description>&lt;h1 id="rendering"&gt;Rendering&lt;a class="anchor" href="#rendering"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;To draw a scene, take a &lt;code&gt;Frame&lt;/code&gt; of it through a camera, then hand the frame to a renderer. The &lt;a href="../widget/"&gt;widget&lt;/a&gt; does all of this for you; this page is for when you want to know what it&amp;rsquo;s doing, or render without it.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;camera&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;DefaultCamera&lt;/span&gt;()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;frame&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Frame&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;camera&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;960&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;640&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; { panic(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;) }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;gpu&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewGPURenderer&lt;/span&gt;()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;defer&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gpu&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Close&lt;/span&gt;()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;img&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;mode&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Snapshot&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;frame&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;nt3d&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;WebGPU&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;gpu&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// mode reports whether WebGPU drew it or software stood in.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="frames"&gt;Frames&lt;a class="anchor" href="#frames"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;A frame is a snapshot: the geometry, world transforms, and visibility of the scene at the moment you took it. It never changes afterwards. To update the picture, change the scene and take a new frame; old frames stay valid, and can even be rendered again. This is what lets rendering run on another goroutine while you keep editing.&lt;/p&gt;</description></item><item><title>Picking</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/picking/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/picking/</guid><description>&lt;h1 id="picking"&gt;Picking&lt;a class="anchor" href="#picking"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;&lt;code&gt;Scene.Pick&lt;/code&gt; finds what lies under a point on the screen: the node, the kind and index of the primitive, your application&amp;rsquo;s datum ID, and the world position hit. In the &lt;a href="../widget/"&gt;widget&lt;/a&gt;, a click sends you a &lt;code&gt;PickMsg&lt;/code&gt; with the same answer.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;hit&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Pick&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;camera&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;frame&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;x&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;y&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;tolerance&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hit&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#9;&lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Println&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;hit&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Name&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;hit&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Index&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;hit&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Position&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#10;&lt;li&gt;Meshes are hit on their faces. Picking sees through a cutout&amp;rsquo;s holes to what&amp;rsquo;s behind them, and treats transparent surfaces as solid.&lt;/li&gt;&#10;&lt;li&gt;Hidden nodes, and nodes outside the view, are never hit.&lt;/li&gt;&#10;&lt;li&gt;Points, boxes, and lines are tested one by one.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="fast-picking-on-big-meshes"&gt;Fast picking on big meshes&lt;a class="anchor" href="#fast-picking-on-big-meshes"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;Testing every triangle takes a few milliseconds even for a 1.5-million-triangle mesh, so a click is answered at once. The first pick of a mesh over 256 triangles also starts building an index (a bounding volume hierarchy) in the background, and after that picks, such as following the mouse for hover, take about a microsecond. The index lasts until the node&amp;rsquo;s geometry changes.&lt;/p&gt;</description></item><item><title>The widget</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/widget/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/widget/</guid><description>&lt;h1 id="the-widget"&gt;The widget&lt;a class="anchor" href="#the-widget"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;&lt;code&gt;nt3dwidget&lt;/code&gt; puts a scene in a &lt;a href="https://github.com/charmbracelet/bubbletea"&gt;Bubble Tea&lt;/a&gt; program. It handles the camera and mouse, schedules renders so slow frames never pile up, falls back when the GPU can&amp;rsquo;t help, and shows the result as Kitty graphics or colored glyphs through &lt;a href="https://github.com/NimbleTerminal/ntpicture"&gt;ntpicture&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;view&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;nt3dwidget&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;New&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;scene&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;defer&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;view&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Close&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The widget draws only the 3D view. Headers, footers, and quitting belong to your program; &lt;a href="../getting-started/#a-first-program"&gt;Getting started&lt;/a&gt; shows a complete one.&lt;/p&gt;&#10;&lt;h2 id="controls"&gt;Controls&lt;a class="anchor" href="#controls"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Input&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Does&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;drag&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;orbit&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;shift-drag&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;pan&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;wheel, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;zoom&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;arrows, &lt;code&gt;h&lt;/code&gt; &lt;code&gt;j&lt;/code&gt; &lt;code&gt;k&lt;/code&gt; &lt;code&gt;l&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;orbit&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;c&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;fit the scene in view (&lt;code&gt;Fit()&lt;/code&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;turn automatic rotation on or off&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;o&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;switch between perspective and orthographic&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;g&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;switch between Kitty graphics and glyphs&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;A click sends your program a &lt;code&gt;PickMsg&lt;/code&gt; saying what was hit; see &lt;a href="../picking/"&gt;Picking&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Demos</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/demos/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/demos/</guid><description>&lt;h1 id="demos"&gt;Demos&lt;a class="anchor" href="#demos"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;The demos are complete programs in &lt;a href="https://github.com/NimbleTerminal/nt3d/tree/main/examples/demos"&gt;&lt;code&gt;examples/demos&lt;/code&gt;&lt;/a&gt;, and the quickest way to see what nt3d does. Each runs in a terminal with &lt;code&gt;task &amp;lt;name&amp;gt;&lt;/code&gt;, and in a browser from the gallery.&lt;/p&gt;&#10;&lt;p&gt;They all share the widget&amp;rsquo;s &lt;a href="../widget/#controls"&gt;controls&lt;/a&gt; (drag to orbit, wheel to zoom, &lt;code&gt;c&lt;/code&gt; to fit, &lt;code&gt;r&lt;/code&gt; to rotate) and a few keys of their own: &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, and &lt;code&gt;3&lt;/code&gt; switch between GPU, software, and wireframe, and &lt;code&gt;q&lt;/code&gt; quits. Each demo lists its other keys along the top of the screen.&lt;/p&gt;</description></item><item><title>Development</title><link>https://nimbleterminal.github.io/nt3d/docs/guide/development/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://nimbleterminal.github.io/nt3d/docs/guide/development/</guid><description>&lt;h1 id="development"&gt;Development&lt;a class="anchor" href="#development"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;h2 id="setting-up"&gt;Setting up&lt;a class="anchor" href="#setting-up"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;nt3d&amp;rsquo;s dependencies, ntcanvas, ntgpuimage, and ntpicture among them, all come through Go modules:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone https://github.com/NimbleTerminal/nt3d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd nt3d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;task ci&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To change ntpicture alongside nt3d, clone it beside nt3d and run &lt;code&gt;task workspace&lt;/code&gt;, which copies &lt;code&gt;go.work.example&lt;/code&gt; to &lt;code&gt;go.work&lt;/code&gt; (git ignores it) so the build uses your checkout instead of the published module.&lt;/p&gt;&#10;&lt;p&gt;The library is one module. The demos in &lt;code&gt;examples/demos&lt;/code&gt; are a second module, so that go-booba and the Bubble Tea fork the browser needs never become requirements of the library.&lt;/p&gt;</description></item></channel></rss>