<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: alphanine</title>
    <description>The latest articles on DEV Community by alphanine (@alphanine).</description>
    <link>https://dev.to/alphanine</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4114904%2Fd497fc70-98e3-4595-968c-6708a736cedb.jpeg</url>
      <title>DEV Community: alphanine</title>
      <link>https://dev.to/alphanine</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alphanine"/>
    <language>en</language>
    <item>
      <title>Turing CAD: An Anime Cel Style on a Cutting Mat</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Thu, 22 Oct 2026 15:24:33 +0000</pubDate>
      <link>https://dev.to/alphanine/turing-cad-an-anime-cel-style-on-a-cutting-mat-38pn</link>
      <guid>https://dev.to/alphanine/turing-cad-an-anime-cel-style-on-a-cutting-mat-38pn</guid>
      <description>&lt;p&gt;Turing CAD can now draw a Gundam frame as an anime cel, standing on a cutting mat.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;中文版在文末，点开即可阅读。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsrecxntlmc7o4xrnxf20.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsrecxntlmc7o4xrnxf20.png" alt="Cel Shaded style on the cutting mat" width="800" height="771"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Chest Frame 34 drawn as an anime cel, standing on a cutting mat with a soft-edged cel shadow.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frpzmqpsyxdauqoqxy9ra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frpzmqpsyxdauqoqxy9ra.png" alt="The cutting mat as a slab on an endless board" width="800" height="771"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Zoomed out, the mat is a 3 mm slab with inked edges and its own shadow on an endless board.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuhnwq3scuws82ej7rs2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuhnwq3scuws82ej7rs2y.png" alt="The shipped cel shadow: flat with a little blur" width="800" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The flat cel shadow at blur 0, .35 (default) and .6.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Painted Gunpla photos often look like anime frames. Could a CAD viewport draw a model that way while you're still modelling it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Situation: three CAD looks and a chest frame that wanted to look painted
&lt;/h2&gt;

&lt;p&gt;Turing CAD is a desktop CAD app for robot parts and Gunpla-style kits, with a small Metal viewport written for modelling. It had three looks: Modeling, Clean Model and Studio. I was modelling a Barbatos chest frame (22 parts) and wanted it to read like a painted kit: white panels, blue-grey shadows, black ink.&lt;/p&gt;

&lt;p&gt;Two small bugs came up first. A design name with a &lt;code&gt;/&lt;/code&gt; in it, &lt;code&gt;Chest Frame 34 · closed thicker sidewalls / shorter inward ledges&lt;/code&gt;, turned the default Export USD folder into a path through folders that don't exist, and the export failed. Clean Model also drew metal pistons as chrome.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy3litbnojexo2tjqfzup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy3litbnojexo2tjqfzup.png" alt="Clean Model drawing the pistons as chrome" width="800" height="797"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before · Clean Model drew the metal pistons as chrome&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fky83heuyn7p2gfpkbg0z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fky83heuyn7p2gfpkbg0z.png" alt="The rods alone, metal reflections on and off" width="800" height="382"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The two hydraulic rods on their own · reflections on (left) · off, as Clean Model now draws them (right)&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Task: a cel look in a live modelling viewport
&lt;/h2&gt;

&lt;p&gt;The look had to live in the viewport itself, not in an offline renderer, and stay interactive on a 22-part assembly. Every choice had to be a setting, and the other styles had to stay as they were.&lt;/p&gt;

&lt;p&gt;What made it hard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cel shading needs hard steps and crisp lines, but the viewport's shadows were soft, filtered shadow maps.&lt;/li&gt;
&lt;li&gt;The outline of a round part is not a B-rep edge, so CAD edges alone can't ink it.&lt;/li&gt;
&lt;li&gt;A floor that reads as a desk has to hold up at any zoom without showing where it ends.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Action: cel bands, a cutting mat, and shadows that stop moving
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What we built
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cel Shaded style.&lt;/strong&gt; One hard step from the lit colour to a cooler shadow tone, a hard white highlight, and metal as two flat bands. Silhouettes are inked with an inverted hull culled by normal (3 px), and CAD edges at 2 px. A round rod shows why both are needed: its long outline isn't a B-rep edge at all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fen0gx332dq9lut6tv5nc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fen0gx332dq9lut6tv5nc.png" alt="The rods with and without the silhouette outline" width="800" height="382"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The same rods in Cel Shaded · only the inverted hull inks the round silhouette&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn01brahk6m6k156n4f36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn01brahk6m6k156n4f36.png" alt="First cel pass on a black kit" width="580" height="720"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;First pass · one shadow step works, but black ink vanishes into black plastic&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fozwby6h2bux4e5spvqce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fozwby6h2bux4e5spvqce.png" alt="The black housing alone in Cel Shaded" width="800" height="383"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Fixed · housing B11 on its own, charcoal in the light so the ink reads · and with material colours off&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cutting mat.&lt;/strong&gt; Painted once with QPainter at true millimetre scale (rulers, protractor, hexagons, a title panel) and sized to the model, A5 and up. It's a 3 mm slab with inked edges lying on an endless board.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ywptk93hq7j2sokacz4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ywptk93hq7j2sokacz4.png" alt="The painted mat texture" width="800" height="566"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The mat's painted layer · its 10/50 mm grid lines are drawn by the shader instead&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyocglgvebbl0gx0h7fr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyocglgvebbl0gx0h7fr5.png" alt="The mat as a slab on an endless board" width="800" height="771"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Zoomed out · a 3 mm slab with inked edges and its own shadow on the board&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cel shadows.&lt;/strong&gt; The filtered shadow value is cut at one half over a single pixel, so a cast shadow becomes a flat shape with a clean edge. The model's own shadow is a separate hard step. On the mat, Shadow softness then blurs that edge back in: after feedback that its maximum wasn't soft enough, it now widens the blur itself, from about 12 to 60 shadow-map texels, sampled in a rotated disc so it grains rather than bands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8eqbnv6nzb51aa4o5lmw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8eqbnv6nzb51aa4o5lmw.png" alt="Cast shadow before and after the hard cut" width="799" height="331"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before: a soft filtered blob · After: one cut at the midpoint, a pixel wide&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5memsy10yo0387zyr3x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5memsy10yo0387zyr3x.png" alt="Housing at softness 0 and 1" width="800" height="383"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Housing B11 under a low light · Shadow softness 0 · and 1, after the wider blur&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Light, grid and settings.&lt;/strong&gt; Light angle and height are sliders in every style. The ground grid is endless and drawn per pixel, stepping 1, 10 and 100 mm as you zoom. Settings are remembered per style, with a one-click Reset to Default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjq2er03iwn96gad6218x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjq2er03iwn96gad6218x.png" alt="Housing under three light directions" width="800" height="254"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;One part, three lights · angle 305° · 215°, with the shadow side towards the camera · a low 20° light&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0e19ii8osk9bfhxzi1zw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0e19ii8osk9bfhxzi1zw.png" alt="The grid ending in a hard line" width="800" height="505"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before · the fixed 120 mm grid ended in a hard line&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc9lum27s2rg699yohihz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc9lum27s2rg699yohihz.png" alt="The grid shrinking to a disc when zoomed out" width="800" height="505"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Still before · a fade fixed in world size shrank the grid to a small disc when zoomed out&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvsk1ufsv84bflvuhtvwr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvsk1ufsv84bflvuhtvwr.png" alt="The endless grid at three zooms" width="800" height="1642"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After · the grid fills the view at every zoom, on Cel and on the dark Modeling style&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj5e4m14jnydvlr0pjziu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj5e4m14jnydvlr0pjziu.png" alt="Display panel controls" width="620" height="1340"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Display · light angle and height, cel shadow, and the mat's shadow&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inspector and Browser.&lt;/strong&gt; Each Inspector tab shows only its own content. The Browser selects and highlights whatever part you pick or hover in the viewport, even through a face, edge or vertex.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgghn32c2j59au8l4sx6m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgghn32c2j59au8l4sx6m.png" alt="Info tab with Joints stacked on top" width="698" height="436"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before · the Info tab opened with the Joints pane on top of it&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fglppk09wmfc7amxvaw6y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fglppk09wmfc7amxvaw6y.png" alt="A selected part with no highlight in the Browser" width="800" height="505"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before · the part is selected in the viewport, the Browser doesn't show it&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6nybwpdvkuj324m2h4t0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6nybwpdvkuj324m2h4t0.png" alt="The Browser selecting the picked part" width="520" height="1170"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After · a face of A11 picked on Geometry selects A11's row&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  What we tested
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every style, rendered for real.&lt;/strong&gt; A headed check opens a window and renders each style through Metal. It grew to 40 steps covering per-style memory, the light sliders moving the shadow and redrawing its map, Reset to Default leaving other styles alone, and the Inspector tabs not stacking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Browser, without a GPU.&lt;/strong&gt; An offscreen selection test hovers and clicks parts and faces, then checks that the right row is highlighted or selected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The real model at each step.&lt;/strong&gt; I rendered Chest Frame 34 after every change, at normal zoom, zoomed out 6× and close up, and compared variants side by side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single parts for single features.&lt;/strong&gt; To show one effect at a time, parts were isolated and rendered under exactly the setting in question: the hydraulic rods for metal and outlines, the black housing B11 for charcoal, softness and light direction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Info tab, profiled.&lt;/strong&gt; The first open took 4.1 s, almost all of it in OCCT's volume and surface integrals for 22 parts, while other tabs switched in 10–25 ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The whole suite before merging.&lt;/strong&gt; 1330 tests. It surfaced two problems of my own, both fixed before the merge (see below). The two failures left are unrelated: 32 new designs without previews yet, and a downloaded model the worktree didn't have.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What surprised us
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Black parts swallowed the ink.&lt;/strong&gt; A black outline on a black part disappears. The fix was to paint dark parts charcoal in the light, the way kit painters treat black plastic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The mat's lines went soft up close.&lt;/strong&gt; The texture only had about 6 pixels per millimetre, so fine lines smeared when magnified. They moved into the shader, which draws them per pixel at any zoom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A redundant integral.&lt;/strong&gt; The mass code integrated each part twice to move inertia to the centroid. While testing a one-pass version, the numbers were off by millions, because OCCT already reports inertia about the centroid. One integral was always enough, and the result is now cached next to the cached solids: 1.4 s the first time, then about 14 ms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "jagged" shadow ink was real.&lt;/strong&gt; Inked shadow edges looked rough until the cause turned out to be the shadows of a ring's gear teeth, not stair-steps from the shadow map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The reference technique lost.&lt;/strong&gt; I tried a painted anime-background shadow: blue multiply, fade from the contact, a warm glow along the edge. The flat cel shadow with a little blur suited the model better, so that's the default and the rest are sliders.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftv7f9w2ow7vlff5s6z4l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftv7f9w2ow7vlff5s6z4l.png" alt="Painted background shadow against the flat one" width="799" height="548"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Tried · the painted background technique (right) against the flat cel shadow (left)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe79p025gromgwbvs4ubl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe79p025gromgwbvs4ubl.png" alt="Three blur levels" width="800" height="228"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Shipped · the flat shadow with a little blur · 0, .35 (default), .6&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shadows kept swinging.&lt;/strong&gt; With the light turning with the camera, shadows moved on every orbit. Cel now keeps a fixed light. Old saved settings kept bringing the moving light back until startup learned to read each style's own memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My own test crashed the suite.&lt;/strong&gt; A new test created a bare &lt;code&gt;QGuiApplication&lt;/code&gt;; a later test found it, tried to build a widget on it, and Qt aborted the whole run. Separately, tutorial steps pointed at sliders that the new Inspector tabs now hid. Both were fixed before the merge.&lt;/p&gt;
&lt;h2&gt;
  
  
  Result: four styles, a mat to stand on, and a suite that runs to the end
&lt;/h2&gt;

&lt;p&gt;On Chest Frame 34 the viewport now reads like a painted kit photo: material colours, blue-grey shadow sides, black ink, and a crisp, slightly blurred shadow on a cutting mat that scales with the model. Opening the Info tab went from 4.1 s to 1.4 s, then about 14 ms. The work is merged to main, and the same look has since been ported to the offline renderer in the Turing Render series.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx85jq1ksi5ra0meaijhy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx85jq1ksi5ra0meaijhy.png" alt="Close-up with the defaults" width="800" height="771"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Close-up · crisp printed grid · cel shadow on the mat and on the model&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Limits remain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The cel look lives in the modelling viewport; Turing CAD can't export a cel render yet.&lt;/li&gt;
&lt;li&gt;The mat's printed templates still soften at extreme close-up.&lt;/li&gt;
&lt;li&gt;The merge also carried another session's uncommitted work, which needs its own review.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next: model the next Barbatos unit in Cel Shaded, and see whether the mat's templates deserve the same per-pixel treatment as its grid.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;
  中文版 · Chinese version（点击展开）
  &lt;p&gt;Turing CAD 现在能把高达骨架画成动画赛璐珞，放在切割垫上。&lt;/p&gt;

&lt;p&gt;涂装好的高达照片常常像一帧动画。CAD 视口能不能在建模的同时，就把模型画成这样？&lt;/p&gt;
&lt;h2&gt;
  
  
  背景：三种 CAD 样式，和一个想看起来像涂装件的胸部骨架
&lt;/h2&gt;

&lt;p&gt;Turing CAD 是一个用来做机器人零件和高达风格模型的桌面 CAD，视口是一个为建模写的小型 Metal 渲染器，原有三种样式：Modeling、Clean Model 和 Studio。我在做 Barbatos 胸部骨架（22 个零件），希望它看起来像涂装好的模型：白色板件、蓝灰阴影、黑色墨线。&lt;/p&gt;

&lt;p&gt;先冒出两个小问题。设计名里带 &lt;code&gt;/&lt;/code&gt;（&lt;code&gt;Chest Frame 34 · closed thicker sidewalls / shorter inward ledges&lt;/code&gt;）时，Export USD 的默认文件夹会变成一串不存在的目录，导出失败；Clean Model 还把金属活塞杆画成了镜面。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy3litbnojexo2tjqfzup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy3litbnojexo2tjqfzup.png" alt="Clean Model 把活塞杆画成镜面" width="800" height="797"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;之前 · Clean Model 把金属活塞杆画成了镜面&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fky83heuyn7p2gfpkbg0z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fky83heuyn7p2gfpkbg0z.png" alt="单独的活塞杆：开和关金属反射" width="800" height="382"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;单独渲染的两根活塞杆 · 左：开启金属反射 · 右：关闭，也就是 Clean Model 现在的画法&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  目标与难点：在实时建模视口里做出赛璐珞质感
&lt;/h2&gt;

&lt;p&gt;效果要直接做在视口里，而不是放进离线渲染器，在 22 个零件的装配体上也要保持实时。每种效果都要是设置项，其他样式原样不动。&lt;/p&gt;

&lt;p&gt;难点有三个：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;赛璐珞需要硬过渡和干净的线条，而视口原来的阴影是柔和的滤波阴影贴图。&lt;/li&gt;
&lt;li&gt;圆形零件的外轮廓不是 B-rep 边，只靠 CAD 边线描不出来。&lt;/li&gt;
&lt;li&gt;像桌面一样的地面要在任何缩放下都成立，而且看不到尽头。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  解决思路与行动：色阶、切割垫，以及不再乱动的阴影
&lt;/h2&gt;
&lt;h3&gt;
  
  
  做了什么
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cel Shaded 样式。&lt;/strong&gt; 亮部到偏冷暗部只有一次硬过渡，加一个硬白高光，金属画成两段平色。外轮廓用按法线剔除的反向外壳描墨线（3 px），CAD 边线 2 px。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fozwby6h2bux4e5spvqce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fozwby6h2bux4e5spvqce.png" alt="单独的黑色外壳 B11" width="800" height="383"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;单独渲染的黑色外壳 B11 · 亮部画成炭灰，墨线清晰可见 · 以及关闭材质颜色的样子&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;切割垫。&lt;/strong&gt; 用 QPainter 按真实毫米尺寸画一次（刻度、量角器、六边形、标题框），按模型大小自动选尺寸，A5 起。它是一块 3 mm 厚、边缘描墨线的板，放在无限延伸的板面上。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyocglgvebbl0gx0h7fr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyocglgvebbl0gx0h7fr5.png" alt="切割垫是一块放在无限板面上的厚板" width="800" height="771"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;缩小视角 · 3 mm 厚、描墨线边缘的垫子，在板面上投下自己的阴影&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;赛璐珞阴影。&lt;/strong&gt; 把滤波后的阴影值在中间以一个像素宽硬切，投影就成了边缘干净的平涂形状；模型自身的投影单独硬切一次。垫子上的 Shadow softness 再把边缘柔化回来：根据“最大值还不够软”的反馈，它现在直接加大模糊半径，从约 12 个阴影贴图纹素到约 60 个，并在旋转的圆盘内采样，出现的是细颗粒而不是色带。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8eqbnv6nzb51aa4o5lmw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8eqbnv6nzb51aa4o5lmw.png" alt="硬切前后的投影" width="799" height="331"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;之前：柔和的一团 · 之后：在中点以一个像素宽硬切&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5memsy10yo0387zyr3x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5memsy10yo0387zyr3x.png" alt="柔和度 0 和 1" width="800" height="383"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;低角度光源下的外壳 B11 · Shadow softness 0 · 以及加大模糊后的 1&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;光源、网格和设置。&lt;/strong&gt; 所有样式都能用滑杆调光源角度和高度。地面网格改为逐像素绘制、无限延伸，随缩放在 1、10、100 mm 之间切换。设置按样式分别记住，可以一键恢复默认。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjq2er03iwn96gad6218x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjq2er03iwn96gad6218x.png" alt="三个光源方向下的外壳" width="800" height="254"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;同一个零件，三种光源 · 角度 305° · 215°，暗面朝向镜头 · 20° 低角度光&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;检查器与 Browser。&lt;/strong&gt; 检查器每个标签页只显示自己的内容；在视口里点选或悬停零件（包括通过面、边、顶点），Browser 会选中并高亮对应的行。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  测了什么
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;每种样式都真实渲染一遍。&lt;/strong&gt; 一个有界面的检查会打开窗口，经 Metal 渲染每种样式。它扩展到了 40 步：按样式记忆、光源滑杆会移动阴影并重画阴影贴图、一键恢复不影响其他样式、检查器标签页不再堆叠。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;不用 GPU 测 Browser。&lt;/strong&gt; 一个离屏选择测试会悬停和点击零件与面，再检查对应的行有没有被高亮或选中。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;每一步都看真实模型。&lt;/strong&gt; 每次改动后都渲染 Chest Frame 34，分正常、缩小 6 倍和特写三种视角，并把变体并排比较：蓝灰与紫色阴影、有无阴影墨线、三档模糊。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;用单个零件展示单个效果。&lt;/strong&gt; 为了一次只看一种效果，把零件单独显示出来，只改要说明的那个设置：活塞杆用来看金属和轮廓线，黑色外壳 B11 用来看炭灰、柔和度和光源方向。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;给 Info 面板做性能分析。&lt;/strong&gt; 第一次打开要 4.1 秒，几乎全部花在 OCCT 为 22 个零件做体积和表面积积分上，而其他标签页切换只要 10–25 毫秒。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;合并前跑完整测试集。&lt;/strong&gt; 共 1330 个测试，查出两个我自己造成的问题，都在合并前修好了（见下文）。剩下的两个失败与这次无关：32 个新设计还没有预览图，另一个测试需要的下载模型不在 worktree 里。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  意外发现
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;黑色零件吞掉了墨线。&lt;/strong&gt; 黑色零件上的黑线看不见。解决办法是让深色零件在亮部变成炭灰，就像涂装师处理黑色塑料那样。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;垫子的线拉近就糊。&lt;/strong&gt; 贴图每毫米只有约 6 个像素，细线放大后会糊成一片。于是把网格线搬进着色器，任何缩放下都逐像素绘制。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;一次多余的积分。&lt;/strong&gt; 质量计算原来对每个零件积分两次，为的是把惯量移到质心。测试只积分一次的版本时，结果差了几百万，原因是 OCCT 给出的惯量本来就以质心为参考点。一次积分一直就够了，结果还缓存在零件实体旁边：第一次 1.4 秒，之后约 14 毫秒。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“锯齿”阴影墨线其实是真的。&lt;/strong&gt; 描了墨线的阴影边缘看起来很粗糙，后来发现那是齿圈齿牙的投影，不是阴影贴图的台阶。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;教程里的方法输了。&lt;/strong&gt; 我试过动画背景式的画法（蓝色正片叠底、离接触点渐淡、边缘暖色光）。略带模糊的平涂阴影更适合这个模型，于是它成了默认值，其余都做成了滑杆。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe79p025gromgwbvs4ubl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe79p025gromgwbvs4ubl.png" alt="三档模糊" width="800" height="228"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;最终采用 · 略带模糊的平涂阴影 · 0、.35（默认）、.6&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;阴影一直在甩。&lt;/strong&gt; 光源跟着相机转时，每次旋转视角阴影都会动。现在 Cel 使用固定光源。旧的保存设置一直把“跟随视角”带回来，直到启动时改为读取每种样式自己的记忆。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;我自己的测试让整个测试集崩了。&lt;/strong&gt; 一个新测试创建了一个单纯的 &lt;code&gt;QGuiApplication&lt;/code&gt;，后面的测试拿到它去创建窗口部件，Qt 直接中止了整轮运行。另一个问题是教程步骤指向的滑杆被新的检查器标签页藏了起来。两个都在合并前修好了。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  结果与下一步：四种样式、一张可以站的垫子，以及能跑完的测试集
&lt;/h2&gt;

&lt;p&gt;在 Chest Frame 34 上，视口现在像一张涂装好的模型照片：材质颜色、蓝灰暗部、黑色墨线，以及落在切割垫上、略带模糊的清晰阴影，垫子随模型大小变化。打开 Info 面板从 4.1 秒变成 1.4 秒，之后约 14 毫秒。这批改动已合并进 main，同样的风格后来也移植到了离线渲染器，见 Turing Render 系列。&lt;/p&gt;

&lt;p&gt;还有这些限制：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;卡通效果只在建模视口里，Turing CAD 还不能导出 cel 渲染图。&lt;/li&gt;
&lt;li&gt;垫子上的印刷图案在极近距离仍会变糊。&lt;/li&gt;
&lt;li&gt;这次合并还带上了另一个会话未提交的工作，需要单独复查。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下一步：用 Cel Shaded 做下一个 Barbatos 部件，再看看垫子上的印刷图案是否也值得像网格一样改成逐像素绘制。&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

</description>
      <category>design</category>
      <category>showdev</category>
      <category>software</category>
    </item>
    <item>
      <title>Turing Sim: IMU and LiDAR Sensors for MuJoCo and Genesis</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Thu, 22 Oct 2026 01:35:07 +0000</pubDate>
      <link>https://dev.to/alphanine/turing-sim-imu-and-lidar-sensors-for-mujoco-and-genesis-24aj</link>
      <guid>https://dev.to/alphanine/turing-sim-imu-and-lidar-sensors-for-mujoco-and-genesis-24aj</guid>
      <description>&lt;p&gt;A LiDAR and an IMU now run beside physics: the scan sweeps the workcell and the IMU reads gravity on the wrist.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;中文版在文末，点开即可阅读。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fam1ktw0hhno2k56hqq5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fam1ktw0hhno2k56hqq5u.png" alt="The same scene playing, before and after · Before" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7k7aaqjvneriorqek4ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7k7aaqjvneriorqek4ef.png" alt="The same scene playing, before and after" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7z7t32mw6kq5mhp9gbb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7z7t32mw6kq5mhp9gbb.gif" alt="The same scene playing, before and after · GIF" width="719" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Before: sensor prims are ignored. After: the LiDAR scan is drawn over the arm and table.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa0tz0hz4ktu0srgzx15j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa0tz0hz4ktu0srgzx15j.png" alt="LiDAR returns coloured by class" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Each return carries the semantic label of the surface it hit: table, cube or unlabelled.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxjno4tfhp1vxdv74xyrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxjno4tfhp1vxdv74xyrb.png" alt="IMU readings on the wrist" width="596" height="1452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Specific force of about 9.8 m/s² against gravity, and MEMS-level gyro noise.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;Turing Sim could simulate robots but not what their sensors see. Isaac Sim's main value for robotics is sensor simulation; Turing Sim had no IMU, no LiDAR and no way to record either. The roadmap made sensors the next stage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fam1ktw0hhno2k56hqq5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fam1ktw0hhno2k56hqq5u.png" alt="Before: the sensor scene playing with no scan drawn" width="800" height="477"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before · the sensor scene playing in the old version: Lidar and Overview prims are in the scene tree, nothing is drawn&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;p&gt;Add IMU and LiDAR sensors that are described in plain USD, work with both physics engines (MuJoCo and Genesis), produce the same kind of data from either, and can be recorded. Discovery must not depend on any particular robot or asset, and adding a sensor must be undoable.&lt;/p&gt;
&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sensors are ordinary USD.&lt;/strong&gt; A sensor is an &lt;code&gt;Xform&lt;/code&gt; with &lt;code&gt;turingSensor:type&lt;/code&gt; and its parameters in the &lt;code&gt;turingSensor:&lt;/code&gt; namespace, mounted on the nearest rigid body or fixed in the world. Reading them needs no schema plugin, so the editor, the MuJoCo host and the separate Genesis runtime see the same sensors. Adding one from a preset is one undoable command.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IMU from each engine's own physics.&lt;/strong&gt; MuJoCo gets accelerometer, gyro and orientation sensors at a site placed by the MJCF conversion, sampled every substep (400 Hz). Genesis uses its IMU per outer step and says when it cannot reach the requested rate. One shared model then adds white noise and bias drift.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LiDAR against what is visible.&lt;/strong&gt; Rays hit the render geometry, not the collision hulls: meshes, primitives and point-instanced copies, grouped by rigid body and moved with each physics frame. MuJoCo's ray caster, which both runtimes ship, does the intersection. Rotating patterns fire column by column through each revolution, so motion distorts a scan the way it does a real spinning sensor. Each return carries intensity, the surface it hit and its semantic label.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Previews and recordings.&lt;/strong&gt; The physics host reports previews on request and records IMU CSVs and organised LiDAR frames with a manifest.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F47wrp2w2hskzelepucqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F47wrp2w2hskzelepucqk.png" alt="Sensor prims drawn at their mounts" width="800" height="477"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Sensor prims Imu, WristCam, Lidar and Overview in the scene, drawn where they are mounted (editing view, from the sensor-panel work)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxjno4tfhp1vxdv74xyrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxjno4tfhp1vxdv74xyrb.png" alt="IMU readings on the wrist" width="596" height="1452"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;IMU on wrist_3_link while playing: acceleration x +0.04, y -9.81, z +0.04 m/s², angular velocity noise around zero&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7k7aaqjvneriorqek4ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7k7aaqjvneriorqek4ef.png" alt="After: the LiDAR scan over the arm and table" width="800" height="477"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After · the same scene playing: the LiDAR scan drawn over the arm and table, coloured by intensity&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa0tz0hz4ktu0srgzx15j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa0tz0hz4ktu0srgzx15j.png" alt="LiDAR returns coloured by class" width="800" height="477"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Each return carries a semantic label: coloured by class (table, cube, unlabelled)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh0oug2kesntedoo7dgf2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh0oug2kesntedoo7dgf2.png" alt="LiDAR returns coloured by distance" width="800" height="477"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Returns coloured by distance, legend 1.52–2.51 m&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj019mti9fn10nr6fws59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj019mti9fn10nr6fws59.png" alt="LiDAR returns coloured by height" width="800" height="477"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Returns coloured by height, legend 0.59–1.35 m&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;Tests check the physics, not just the plumbing. An IMU at rest reads +9.81 m/s² upward, tilted 90° it reads along +Y, in free fall it reads about zero, and on a turntable at 90°/s it reads 1.571 rad/s within 1%. Genesis gives the same readings. LiDAR ranges on a flat wall are exact.&lt;/p&gt;

&lt;p&gt;On the workcell the scan costs about 4.7 ms per 10 ms step on MuJoCo, which stays real time. Genesis GPU goes from 10.7 to 18.5 ms per step, so it plays at about half speed.&lt;/p&gt;

&lt;p&gt;The captures run the same scene before and after: the old version ignores the sensor prims, the new one draws the scan. Cameras came next and are in the following post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7z7t32mw6kq5mhp9gbb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7z7t32mw6kq5mhp9gbb.gif" alt="Demo: LiDAR colouring while playing" width="719" height="429"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo · the scan while playing, switching between intensity, distance, height and class colouring&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;
  中文版 · Chinese version（点击展开）
  &lt;p&gt;LiDAR 和 IMU 现在随物理一起运行：激光扫过工作站，手腕上的 IMU 读出重力。&lt;/p&gt;
&lt;h2&gt;
  
  
  背景
&lt;/h2&gt;

&lt;p&gt;Turing Sim 能仿真机器人，但不能仿真它们的传感器看到了什么。Isaac Sim 对机器人最重要的价值就在传感器仿真，而 Turing Sim 没有 IMU、没有 LiDAR，也没法录制这些数据。路线图把传感器定为下一个阶段。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fam1ktw0hhno2k56hqq5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fam1ktw0hhno2k56hqq5u.png" alt="改前：播放时没有画出扫描" width="800" height="477"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;改前 · 旧版本播放同一场景：场景树里有 Lidar 和 Overview，视口里什么都没画&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  目标与难点
&lt;/h2&gt;

&lt;p&gt;加入 IMU 和 LiDAR：用普通 USD 描述，两个物理引擎（MuJoCo 和 Genesis）都能用，两边产出同一种数据，并且可以录制。识别传感器不能依赖特定的机器人或资产，添加传感器必须可以撤销。&lt;/p&gt;
&lt;h2&gt;
  
  
  解决思路与行动
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;传感器就是普通的 USD。&lt;/strong&gt; 一个传感器就是一个 &lt;code&gt;Xform&lt;/code&gt;，带 &lt;code&gt;turingSensor:type&lt;/code&gt; 和 &lt;code&gt;turingSensor:&lt;/code&gt; 命名空间下的参数，挂在最近的刚体上，或者固定在世界里。读取不需要 schema 插件，所以编辑器、MuJoCo 宿主和独立的 Genesis 运行时看到的是同一组传感器。从预设添加一个传感器是一次可撤销的操作。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IMU 用各自引擎的物理结果。&lt;/strong&gt; MuJoCo 由 MJCF 转换在挂载位置放一个 site，配上加速度计、陀螺仪和姿态传感器，每个子步采样一次（400 Hz）。Genesis 每个外层步读一次它自己的 IMU，达不到请求的频率时会说明。然后由同一套模型叠加白噪声和零偏漂移。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LiDAR 打在可见几何上。&lt;/strong&gt; 射线打的是渲染几何，不是碰撞凸包：网格、基本体和点实例化的副本，按刚体分组，随每一帧物理结果移动。两个运行时都带有 MuJoCo，用它的射线检测求交。旋转式扫描在每圈里逐列发射，所以运动会让扫描变形，和真实的旋转雷达一样。每个回波都带强度、命中的表面和它的语义标签。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;预览和录制。&lt;/strong&gt; 物理宿主按请求返回预览，并把 IMU 写成 CSV、把 LiDAR 写成按行列组织的帧，附带一份 manifest。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxjno4tfhp1vxdv74xyrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxjno4tfhp1vxdv74xyrb.png" alt="手腕上的 IMU 读数" width="596" height="1452"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;手腕上的 IMU：加速度 x +0.04、y -9.81、z +0.04 m/s²，角速度噪声在零附近&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7k7aaqjvneriorqek4ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7k7aaqjvneriorqek4ef.png" alt="改后：LiDAR 扫描画在机械臂和桌面上" width="800" height="477"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;改后 · 同一场景播放时，LiDAR 扫描点画在机械臂和桌面上，按强度上色&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  结果与下一步
&lt;/h2&gt;

&lt;p&gt;测试检查的是物理结果，不只是数据通路。静止的 IMU 向上读到 +9.81 m/s²；绕 X 轴倾斜 90° 后读数在 +Y 方向；自由落体时约为零；放在 90°/s 的转台上读到 1.571 rad/s，误差在 1% 以内。Genesis 的读数一致。LiDAR 打在平墙上的距离是精确的。&lt;/p&gt;

&lt;p&gt;在工作站场景里，MuJoCo 每个 10 ms 步多花约 4.7 ms，仍然实时。Genesis GPU 从每步 10.7 ms 变成 18.5 ms，大约半速播放。&lt;/p&gt;

&lt;p&gt;截图用同一个场景对比：旧版本忽略传感器 prim，新版本画出扫描点云。相机在下一篇。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7z7t32mw6kq5mhp9gbb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7z7t32mw6kq5mhp9gbb.gif" alt="动图：播放中的 LiDAR 上色" width="719" height="429"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;动图 · 播放中切换强度、距离、高度、类别四种上色&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

</description>
      <category>hardware</category>
      <category>robotics</category>
      <category>software</category>
    </item>
    <item>
      <title>Turing Render: A Cel Style for Anime-Look Robot Animation</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Wed, 21 Oct 2026 15:30:19 +0000</pubDate>
      <link>https://dev.to/alphanine/turing-render-a-cel-style-for-anime-look-robot-animation-3e9o</link>
      <guid>https://dev.to/alphanine/turing-render-a-cel-style-for-anime-look-robot-animation-3e9o</guid>
      <description>&lt;p&gt;Turing RT now renders USD scenes in the Cel style of Turing CAD — the same SO-101 view in realtime ray tracing and in cel. / Turing RT 现在能把 USD 场景渲染成 Turing CAD 的三渲二风格：同一视角下的实时光追与三渲二对比。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsubbxsvpa99ime0ggx5j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsubbxsvpa99ime0ggx5j.png" alt="Cel style vs realtime ray tracing on the SO-101 arm" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same SO-101 view rendered by Turing RT in realtime ray tracing and in the new cel style (1920x1280 renders, cropped).&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;Turing CAD already had a &lt;strong&gt;Cel Shaded&lt;/strong&gt; viewport style: parts in their material colours, one hard step from light to a cooler shadow tone, a hard white highlight, dark parts painted charcoal, metal as two flat bands, and black ink. Looking at it, the user saw that this anime look could be used for animation. The CAD viewport is a small Metal rasterizer built for modelling, though. It draws CAD data directly and does not read USD, so Turing Sim, where robots actually move, had no way to show that look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;p&gt;We first had to decide where the look belongs. The user asked whether the CAD rasterizer could become a standalone renderer, the way Mantra was for Houdini. We decided against it. A standalone renderer is fine, but Mantra's weakness was its private scene format, which Houdini later fixed by moving to Karma, a Hydra render delegate that reads USD. Turing RT is already that kind of renderer and already runs inside Turing Sim. So the plan was to port the &lt;em&gt;shading rules&lt;/em&gt; into Turing RT, not the CAD rasterizer's code.&lt;/p&gt;

&lt;p&gt;What made it hard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The look depends on hard steps and crisp lines. A path tracer's noise and denoiser would smear both.&lt;/li&gt;
&lt;li&gt;Ink needs to know where one surface ends and another starts. Turing RT has no B-rep face ids and no rasterized G-buffer.&lt;/li&gt;
&lt;li&gt;It has to stay fast enough to scrub and play animation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cel style in Turing RT (&lt;code&gt;turing:style = 1&lt;/code&gt;).&lt;/strong&gt; A new two-kernel path. &lt;code&gt;celSurface&lt;/code&gt; traces one camera ray and one shadow ray per pixel and writes position, part, normal and the cel tone. &lt;code&gt;celInk&lt;/code&gt; compares each pixel with its neighbours. A neighbour that is nearer, and not on the pixel's tangent plane, puts the pixel behind an outline. That gives silhouettes and occlusion lines on the far side, like the inverted hull Turing CAD draws (3 px). Normals that bend more than 35°, and two parts meeting flush, give a centred edge (2 px). Every pixel in a pass uses the same sub-pixel offset, so neighbours are compared like with like. 32 passes are averaged for anti-aliasing. Shadows are cast by the scene's key light: the first distant light, else another light, else the built-in sun.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cel Shaded mode in Turing Sim.&lt;/strong&gt; A fourth entry in the render-mode menu sets the style on Turing RT, in both the native viewport and the in-viewport Hydra fallback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Command line.&lt;/strong&gt; &lt;code&gt;turing-render render --style cel --frame N&lt;/code&gt; renders one animation frame from USD.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Settings &lt;code&gt;turing:celShadow&lt;/code&gt;, &lt;code&gt;turing:outlineWidth&lt;/code&gt; and &lt;code&gt;turing:edgeWidth&lt;/code&gt; are declared in the delegate and the Python package together, and an existing test keeps their defaults in step. Validation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new GPU test checks flat face colour, a cooler shadow tone, a cast shadow on the ground, ink at the silhouette, and no ink when the widths are 0.&lt;/li&gt;
&lt;li&gt;Turing RT's existing 58 tests still pass.&lt;/li&gt;
&lt;li&gt;Turing Sim's render-mode tests pass. One unrelated Sim test was already failing on stale expectations and was left alone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;On the SO-101 follower arm at 1920×1280, a cel pass takes about 2.2 ms on the development Mac. The anti-aliased image converges in 0.2 s; realtime ray tracing took 6.4 s to settle on the same view. The arm reads like an animation frame. The parts keep their colours, the servos are charcoal rather than black, and ink lands on part boundaries and hard creases but not on fillets.&lt;/p&gt;

&lt;p&gt;Limits remain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tangent B-rep edges get no ink, because edges come from shading normals rather than face ids.&lt;/li&gt;
&lt;li&gt;Glass and opacity cutouts are drawn opaque.&lt;/li&gt;
&lt;li&gt;The light is fixed in the world; there is no light-follows-view option yet.&lt;/li&gt;
&lt;li&gt;Sim's Cel Shaded mode has passed tests but has not yet been tried in the running app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next: try it while a Sim animation plays, then decide whether B-rep face ids are worth exporting for tangent edges.&lt;/p&gt;




&lt;h2&gt;
  
  
  背景
&lt;/h2&gt;

&lt;p&gt;Turing CAD 已经有一个 &lt;strong&gt;Cel Shaded&lt;/strong&gt; 视口风格：零件用材质颜色，亮部到偏冷的暗部只有一次硬过渡，有一个硬白高光；深色零件涂成炭灰，金属只画两段平色，再加黑色墨线。用户看了以后觉得这种三渲二风格完全可以拿来做动画。但 CAD 视口是一个为建模写的小型 Metal 光栅器，直接画 CAD 数据，不读 USD。机器人真正动起来的地方是 Turing Sim，那里看不到这种风格。&lt;/p&gt;

&lt;h2&gt;
  
  
  目标与难点
&lt;/h2&gt;

&lt;p&gt;先要决定这种风格放在哪里。用户问过：能不能把 CAD 的渲染器独立出来，像 Houdini 当年的 Mantra 那样？我们的结论是不这样做。独立渲染器本身没问题；Mantra 的短板是它的专用场景格式，Houdini 后来换成了 Karma，一个读 USD 的 Hydra 渲染代理。Turing RT 已经是这种渲染器，而且已经跑在 Turing Sim 里。所以方案是把&lt;strong&gt;着色规则&lt;/strong&gt;移植进 Turing RT，而不是搬 CAD 光栅器的代码。&lt;/p&gt;

&lt;p&gt;难点有三个：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;这种风格靠硬过渡和干净的线条，路径追踪的噪声和降噪会把两者都抹糊。&lt;/li&gt;
&lt;li&gt;描线需要知道一个面在哪里结束、另一个面从哪里开始，而 Turing RT 没有 B-rep 面编号，也没有光栅化的 G-buffer。&lt;/li&gt;
&lt;li&gt;速度要快到能拖动时间轴、播放动画。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  解决思路与行动
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Turing RT 的三渲二风格（&lt;code&gt;turing:style = 1&lt;/code&gt;）。&lt;/strong&gt; 新增一条由两个 kernel 组成的渲染路径。&lt;code&gt;celSurface&lt;/code&gt; 每像素打一条相机光线和一条阴影光线，写出位置、所属零件、法线和三渲二颜色。&lt;code&gt;celInk&lt;/code&gt; 把每个像素和周围像素比较。如果邻居更近，而且不在本像素的切平面上，本像素就落在轮廓线后面。这样外轮廓和遮挡线画在远侧，效果和 Turing CAD 的反向外壳一样（3 px）。法线夹角超过 35° 的硬折边、以及两个零件平齐贴合的地方，画居中的边线（2 px）。同一遍里所有像素用同一个亚像素偏移，所以相邻像素的比较是对等的；累加 32 遍完成抗锯齿。投影由场景主光产生：优先取第一个平行光，没有就取其他灯，再没有就用内置太阳。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turing Sim 的 Cel Shaded 模式。&lt;/strong&gt; 渲染模式菜单里多了第四项，在原生视口和视口内 Hydra 两种路径下都会把这个风格设置给 Turing RT。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;命令行。&lt;/strong&gt; &lt;code&gt;turing-render render --style cel --frame N&lt;/code&gt; 可以从 USD 渲染单帧动画。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;turing:celShadow&lt;/code&gt;、&lt;code&gt;turing:outlineWidth&lt;/code&gt;、&lt;code&gt;turing:edgeWidth&lt;/code&gt; 三个设置在渲染代理和 Python 包里同时声明，已有测试保证两边的默认值一致。验证情况：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;新增一个 GPU 测试，检查面颜色是平的、暗部偏冷、地面上有投影、轮廓处有墨线、线宽设为 0 时没有墨线。&lt;/li&gt;
&lt;li&gt;Turing RT 原有的 58 个测试仍全部通过。&lt;/li&gt;
&lt;li&gt;Turing Sim 的渲染模式测试通过。Sim 另有一个测试原本就因期望值过时而失败，与这次改动无关，这次没有动它。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  结果与下一步
&lt;/h2&gt;

&lt;p&gt;在开发用的 Mac 上，SO-101 从动臂 1920×1280 渲染一遍约 2.2 ms，抗锯齿后的图 0.2 s 收敛；同一视角下实时光追要 6.4 s 才稳定。渲出来的机械臂像一帧动画：零件保留各自颜色，舵机是炭灰而不是纯黑，墨线画在零件交界和硬折边上，圆角上没有。&lt;/p&gt;

&lt;p&gt;还有这些限制：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;B-rep 的相切边不画线，因为线条来自着色法线而不是面编号。&lt;/li&gt;
&lt;li&gt;玻璃和镂空按不透明处理。&lt;/li&gt;
&lt;li&gt;光源固定在世界中，还没有“光随视角”的选项。&lt;/li&gt;
&lt;li&gt;Sim 的 Cel Shaded 模式已经通过测试，但还没有在运行中的应用里实际试过。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下一步：在 Sim 播放动画时实际试用，再决定是否值得为相切边导出 B-rep 面编号。&lt;/p&gt;

</description>
      <category>3d</category>
      <category>animation</category>
      <category>design</category>
    </item>
    <item>
      <title>Turing Sim: A Content Drawer and Panels That Keep Their Shortcuts</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Wed, 21 Oct 2026 14:11:51 +0000</pubDate>
      <link>https://dev.to/alphanine/turing-sim-a-content-drawer-and-panels-that-keep-their-shortcuts-1g7d</link>
      <guid>https://dev.to/alphanine/turing-sim-a-content-drawer-and-panels-that-keep-their-shortcuts-1g7d</guid>
      <description>&lt;p&gt;Cmd-Shift-E now opens and puts away the Content panel, like a drawer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;中文版在文末，点开即可阅读。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2b0sydyhqzzojne1jjpu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2b0sydyhqzzojne1jjpu.png" alt="Pressing Cmd-Shift-E with Content open, before and after · Before" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkxmr7yvb5j4q6j565vel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkxmr7yvb5j4q6j565vel.png" alt="Pressing Cmd-Shift-E with Content open, before and after" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbimkr3gez4wiqy44haw2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbimkr3gez4wiqy44haw2.gif" alt="Pressing Cmd-Shift-E with Content open, before and after · GIF" width="759" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Before: the panel stayed. After: it is put away and the viewport gets the room.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;Turing Sim's Content panel, the example and asset browser, opens with Cmd-Shift-E. Once open, the same shortcut did nothing, so it took several clicks to get the viewport's room back. The bottom panels' top edge could only be dragged down: with no central widget, Qt had no room above to grow into. Two shortcuts had also leaked out of their panels: Ctrl-Return in the chat input sent a message from anywhere, and Return or Cmd-F in Content acted from fields in other panels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;p&gt;Make Content behave like a drawer, let the bottom edge move both ways, and keep each panel's shortcuts inside that panel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cmd-Shift-E toggles.&lt;/strong&gt; With the Content panel in view, the shortcut puts it away; pressed again, it reopens at the height it was put away at.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The bottom edge drags both ways.&lt;/strong&gt; Dragging it resizes the panels on both sides, and a taller window gives its height to the viewport row instead of the bottom panels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A short Content dock stays usable.&lt;/strong&gt; Its panes scroll and its footer hides below a minimum height.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shortcuts stay local.&lt;/strong&gt; PySide6 silently ignores &lt;code&gt;QShortcut(context=...)&lt;/code&gt; when a slot is passed, which had left them window-wide. They now set their context explicitly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The commit was tested in isolation: 80 passed and 6 native docking tests skipped offscreen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;The captures press the shortcut with Content open. The old version keeps the panel; the new one puts it away. The demo opens and closes it twice. A native check for the shortcuts was added (&lt;code&gt;verify_panel_key_focus.py&lt;/code&gt;) but was not run in this session.&lt;/p&gt;




&lt;p&gt;&lt;/p&gt;
  中文版 · Chinese version（点击展开）
  &lt;p&gt;⌘⇧E 现在可以像抽屉一样打开和收起 Content 面板。&lt;/p&gt;
&lt;h2&gt;
  
  
  背景
&lt;/h2&gt;

&lt;p&gt;Turing Sim 的 Content 面板（示例和资产浏览器）用 ⌘⇧E 打开。打开之后再按同一个快捷键没有任何反应，要点好几下才能把视口的空间要回来。底部面板的上边缘只能往下拖：窗口没有中央控件，Qt 在上方没有可以扩展的空间。还有两个快捷键越出了自己的面板：聊天输入框的 Ctrl-Return 在任何地方都会发送消息，Content 里的 Return 和 ⌘F 会在其他面板的输入框里触发。&lt;/p&gt;
&lt;h2&gt;
  
  
  目标与难点
&lt;/h2&gt;

&lt;p&gt;让 Content 像抽屉一样开关，让底部边缘可以上下拖，并让每个面板的快捷键只在自己面板里生效。&lt;/p&gt;
&lt;h2&gt;
  
  
  解决思路与行动
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;⌘⇧E 可以开关。&lt;/strong&gt; Content 面板可见时按下会收起；再按一次，会按收起时的高度重新打开。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;底部边缘可以上下拖。&lt;/strong&gt; 拖动时同时调整上下两侧面板的大小；窗口变高时，多出的高度给视口那一行，而不是底部面板。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;矮的 Content 面板依然可用。&lt;/strong&gt; 窗格可以滚动，低于最小高度时页脚会隐藏。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;快捷键只在本面板生效。&lt;/strong&gt; 传入 slot 时 PySide6 会悄悄忽略 &lt;code&gt;QShortcut(context=...)&lt;/code&gt;，导致这些快捷键变成全窗口生效。现在改为显式设置 context。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这个 commit 单独测试：80 项通过，6 项原生停靠测试在离屏模式下跳过。&lt;/p&gt;
&lt;h2&gt;
  
  
  结果与下一步
&lt;/h2&gt;

&lt;p&gt;截图里都是在 Content 打开时按下快捷键。旧版本面板还在；新版本面板被收起了。动图里开关了两次。这个 commit 还加了一个原生窗口里的快捷键检查脚本（&lt;code&gt;verify_panel_key_focus.py&lt;/code&gt;），本次会话没有运行它。&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>tools</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Turing Sim: Joint Edits That Apply While You Type</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Tue, 20 Oct 2026 01:36:25 +0000</pubDate>
      <link>https://dev.to/alphanine/turing-sim-joint-edits-that-apply-while-you-type-80m</link>
      <guid>https://dev.to/alphanine/turing-sim-joint-edits-that-apply-while-you-type-80m</guid>
      <description>&lt;p&gt;Typing a new joint target now moves the arm right away, without pressing Apply.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;中文版在文末，点开即可阅读。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwnkli1yy1u9lwjk2bklj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwnkli1yy1u9lwjk2bklj.png" alt="Shoulder target typed while playing, before and after · Before" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1jgxuqq6po7vp7o52753.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1jgxuqq6po7vp7o52753.png" alt="Shoulder target typed while playing, before and after" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5anlbbigh4o34ezkep7m.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5anlbbigh4o34ezkep7m.gif" alt="Shoulder target typed while playing, before and after · GIF" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Both captures typed -135° into the shoulder target; only the new version moved the arm.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;The Robot panel's joint controller lets you choose a joint, a mode (position, velocity or passive), a target and gains while physics runs. Every change had to be confirmed with "Apply joint command". Typing a new shoulder angle did nothing until you found that button, which made exploring a pose slow. A second problem: each state update rebuilt the joint list and jumped back to the first joint, losing the one you were driving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;p&gt;Make edits take effect as you make them, without flooding the physics host with a command per keystroke, and keep the driven joint selected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Live apply with a short pause.&lt;/strong&gt; A change to the target, stiffness, damping, force limit or mode starts a 40 ms timer, so a held arrow key or a typed number becomes one command rather than many. The button stays for re-sending the same command.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Showing is not commanding.&lt;/strong&gt; Selecting a joint loads its current settings into the fields without sending anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The driven joint stays selected.&lt;/strong&gt; When the host reports its joints again, the panel keeps the current joint instead of resetting to the first.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The commit was tested in isolation: 15 tests passed, including a new test for the live behaviour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;In both captures the shoulder target was set to -135° by typing, with Apply never pressed. In the old version the arm stayed where it was. In the new one it swings down to the new angle. The demo shows several edits in a row.&lt;/p&gt;




&lt;p&gt;&lt;/p&gt;
  中文版 · Chinese version（点击展开）
  &lt;p&gt;输入新的关节目标后机械臂立刻动，不用再点 Apply。&lt;/p&gt;
&lt;h2&gt;
  
  
  背景
&lt;/h2&gt;

&lt;p&gt;Robot 面板的关节控制器可以在物理运行时选择关节、模式（位置、速度、被动）、目标值和增益。以前每次修改都要再点 "Apply joint command" 确认。输入新的肩部角度后什么都不会发生，直到你找到那个按钮，所以试姿态很慢。另一个问题是每次状态更新都会重建关节列表，跳回第一个关节，正在驱动的那个就丢了。&lt;/p&gt;
&lt;h2&gt;
  
  
  目标与难点
&lt;/h2&gt;

&lt;p&gt;让修改即时生效，但不能每按一次键就给物理进程发一条指令；同时保持当前驱动的关节处于选中状态。&lt;/p&gt;
&lt;h2&gt;
  
  
  解决思路与行动
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;短暂停顿后自动生效。&lt;/strong&gt; 修改目标、刚度、阻尼、力矩上限或模式时启动一个 40 ms 的计时器，所以按住方向键或输入一个数字只会变成一条指令。按钮保留，用来重发同一条指令。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;查看不等于下指令。&lt;/strong&gt; 选中一个关节只会把它当前的设置填进输入框，不发送任何指令。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;正在驱动的关节保持选中。&lt;/strong&gt; 宿主再次报告关节列表时，面板保留当前关节，不再重置到第一个。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这个 commit 单独测试：15 项通过，其中包括新增的即时生效测试。&lt;/p&gt;
&lt;h2&gt;
  
  
  结果与下一步
&lt;/h2&gt;

&lt;p&gt;两张截图里都是直接输入把肩部目标设为 -135°，都没有点 Apply。旧版本里机械臂纹丝不动；新版本里它直接摆到了新角度。动图里演示了连续几次修改。&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

</description>
      <category>development</category>
      <category>robotics</category>
      <category>software</category>
    </item>
    <item>
      <title>Turing Sim: Camera Ground Truth from One Hydra Render</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Mon, 19 Oct 2026 03:07:47 +0000</pubDate>
      <link>https://dev.to/alphanine/turing-sim-camera-ground-truth-from-one-hydra-render-2kkp</link>
      <guid>https://dev.to/alphanine/turing-sim-camera-ground-truth-from-one-hydra-render-2kkp</guid>
      <description>&lt;p&gt;Camera sensors now render RGB with pixel-aligned depth, normals, object and class masks, and boxes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;中文版在文末，点开即可阅读。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu0jtvqhylk341s7w0cyd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu0jtvqhylk341s7w0cyd.png" alt="Camera outputs in the Robot panel" width="596" height="1452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fckq569ic3mdput3pwkdx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fckq569ic3mdput3pwkdx.gif" alt="Camera outputs in the Robot panel · GIF" width="600" height="1462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The overview camera switching between RGB, depth, normals, objects, classes and boxes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fibsbkhvnj1wh0b4sv0xy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fibsbkhvnj1wh0b4sv0xy.png" alt="Object segmentation" width="596" height="1452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One colour per object: each UR10 link, the table and the cube.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7g1kqxpp9p182kyz2ai6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7g1kqxpp9p182kyz2ai6.png" alt="Depth" width="596" height="1452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Metric depth along the optical axis, shown with an inverse-depth Turbo ramp.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;After IMU and LiDAR, the sensor stage needed cameras: not only pictures, but the ground truth that robot learning uses, such as depth, segmentation and boxes, aligned exactly with the image. Turing Sim renders with Storm (rasterised) or Turing RT (ray traced) through Hydra. Neither exposed its depth or id buffers to Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;p&gt;Give camera sensors every output from a single render, so all of them describe the same pixels, with accuracy that can be checked. They also had to run in the editor without degrading the viewport.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cameras are USD cameras.&lt;/strong&gt; A camera sensor is a standard &lt;code&gt;UsdGeom.Camera&lt;/code&gt; with &lt;code&gt;turingSensor:type = "camera"&lt;/code&gt;. Presets set the lens from a field of view and the image's aspect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the AOVs back.&lt;/strong&gt; A small native module reads Hydra's color, depth, primId and instanceId buffers and decodes ids to scene paths, including instance proxies. It works with Storm and with Turing RT.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A separate render process.&lt;/strong&gt; Integer ids need a render without multisampling, and Hydra reads that setting once per process, so cameras render in their own worker and the editor's viewport keeps its antialiasing. Storm's eye-normal buffer turned out to clamp negative components, so normals are rebuilt from depth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From buffers to ground truth.&lt;/strong&gt; RGB is averaged over odd supersampling; ground truth uses each pixel's centre sample. Depth is converted to metres along the optical axis. An object is the nearest labelled prim, else its rigid body. Tight and amodal 2D boxes and oriented 3D boxes come with OpenCV intrinsics and the camera's pose.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;On a wall 4 m away, depth is within 2 mm wherever the wall shows, and back-projected pixels land on the wall plane within 2 mm. Storm and Turing RT agree pixel for pixel on the masks, with 0.01 mm median depth difference.&lt;/p&gt;

&lt;p&gt;A 640×480 frame with every output takes 36 ms; with 3×3 antialiasing, 73 ms. The presets' rates follow these measurements.&lt;/p&gt;

&lt;p&gt;The captures show the editor's camera preview switching between outputs on the UR10 workcell. A follow-up commit stopped box names from piling up on the robot's links. Frames are best effort in the editor; exact-rate capture belongs to the synthetic-data pipeline, which is next.&lt;/p&gt;




&lt;p&gt;&lt;/p&gt;
  中文版 · Chinese version（点击展开）
  &lt;p&gt;相机传感器现在能渲染 RGB，并输出逐像素对齐的深度、法线、物体和类别分割以及检测框。&lt;/p&gt;
&lt;h2&gt;
  
  
  背景
&lt;/h2&gt;

&lt;p&gt;做完 IMU 和 LiDAR，传感器阶段还需要相机：不只是画面，还有机器人学习要用的真值，比如深度、分割、检测框，而且要和画面严格对齐。Turing Sim 通过 Hydra 用 Storm（光栅化）或 Turing RT（光线追踪）渲染，两者都没有把深度和 ID 缓冲交给 Python。&lt;/p&gt;
&lt;h2&gt;
  
  
  目标与难点
&lt;/h2&gt;

&lt;p&gt;让相机传感器从一次渲染里得到全部输出，保证所有输出描述的是同一组像素，而且精度可以检验。同时要能在编辑器里运行，不影响视口画质。&lt;/p&gt;
&lt;h2&gt;
  
  
  解决思路与行动
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;相机就是 USD 相机。&lt;/strong&gt; 相机传感器是一个标准的 &lt;code&gt;UsdGeom.Camera&lt;/code&gt;，带 &lt;code&gt;turingSensor:type = "camera"&lt;/code&gt;。预设根据视场角和画面宽高比设置镜头。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;读回 AOV。&lt;/strong&gt; 一个小的原生模块读取 Hydra 的颜色、深度、primId 和 instanceId 缓冲，并把 ID 解码成场景路径（包括实例代理）。Storm 和 Turing RT 都适用。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;单独的渲染进程。&lt;/strong&gt; 整数 ID 必须在关闭多重采样的情况下渲染，而 Hydra 每个进程只读一次这个设置，所以相机在自己的 worker 进程里渲染，编辑器视口照样保留抗锯齿。另外发现 Storm 的视空间法线缓冲会把负分量截掉，所以法线改为由深度重建。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;从缓冲到真值。&lt;/strong&gt; RGB 按奇数倍超采样取平均；真值取每个像素的中心采样。深度换算成沿光轴的米数。物体取最近的带标签 prim，没有标签就取所属刚体。输出紧框和投影框两种 2D 框、有向 3D 框，以及 OpenCV 内参和相机位姿。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  结果与下一步
&lt;/h2&gt;

&lt;p&gt;对 4 米外的一面墙，只要墙出现的像素，深度误差都在 2 mm 以内，反投影回去的点也落在墙面 2 mm 以内。Storm 和 Turing RT 的分割掩码逐像素一致，深度中位差 0.01 mm。&lt;/p&gt;

&lt;p&gt;带全部输出的 640×480 一帧要 36 ms；开 3×3 抗锯齿要 73 ms。预设的帧率就是按这些实测值定的。&lt;/p&gt;

&lt;p&gt;截图展示的是编辑器里的相机预览在 UR10 工作站上切换各种输出。后续一个 commit 修好了机器人连杆上框标签互相重叠的问题。编辑器里的帧是尽力而为；按精确帧率采集属于合成数据流程，那是下一步。&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>robotics</category>
    </item>
    <item>
      <title>Turing Render: From Grey Fallbacks to MaterialX Glaze, Gold and Fitted Studio Light</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Mon, 19 Oct 2026 03:07:32 +0000</pubDate>
      <link>https://dev.to/alphanine/turing-render-from-grey-fallbacks-to-materialx-glaze-gold-and-fitted-studio-light-n9f</link>
      <guid>https://dev.to/alphanine/turing-render-from-grey-fallbacks-to-materialx-glaze-gold-and-fitted-studio-light-n9f</guid>
      <description>&lt;p&gt;Real Turing RT renders: the MaterialX Teapot and Lion with their materials, and coarse meshes without black rims.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyrwlpnmb1jucutte746e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyrwlpnmb1jucutte746e.png" alt="MaterialX Teapot and Lion: from grey fallback to their materials" width="799" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ASWF MaterialX Teapot and Lion now render their glaze, brass, gold and wood under a studio fitted to the official images (left: official render, middle: before, right: after).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F33od9iw9p5ykf2khhbwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F33od9iw9p5ykf2khhbwv.png" alt="Coarse smooth meshes without black rims or stepped terminators" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Low-poly smooth-shaded spheres lose their black silhouette rims, and the shadow terminator of a 12-segment sphere becomes smooth.&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;Turing RT is the Metal path tracer behind Turing Sim, our robot simulator. It already had OpenPBR-style layered materials, HDRI importance sampling and an AI denoiser, but the question of the day was blunt: why do the renders still not look good?&lt;/p&gt;

&lt;p&gt;The first finding was about content, not the renderer. The robot workcell scenes had no lights and no materials, only display colours, so path tracing and realtime ray tracing produced nearly identical images: there was nothing to bounce off and nothing glossy to reflect. Adding a floor, two area lights and a few real materials to the same robot immediately showed soft shadows and glossy reflections. That moved the work to the cases where the renderer itself was wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Make glass and metal hold up on real assets, starting with a material test scene.&lt;/li&gt;
&lt;li&gt;Support the NVIDIA MaterialX Teapot and Lion from the ASWF Digital Production Example Library, which rendered completely grey.&lt;/li&gt;
&lt;li&gt;Explain why their colours still differed from the official renders, and close as much of that gap as could be measured.&lt;/li&gt;
&lt;li&gt;Make Turing Sim always use the latest renderer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The constraint throughout was the project rule that every rendering change ships with a measurement against a reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dark rims on smooth low-poly meshes.&lt;/strong&gt; Metal spheres showed black rims. Swapping them for 256×128 meshes made the rims vanish, which pointed at interpolated normals on coarse facets: silhouettes were mistaken for back faces and reflections went into the surface. The fix decides front and back from the face, keeps opaque shading normals valid (the approach Cycles uses, with its quadratic root checked numerically over 200,000 random cases), and lifts shadow rays near the terminator onto the smooth surface (Hanika 2021). In a white-furnace test, a 12-segment mirror sphere went from 3.3% dark pixels to none, a strongly normal-mapped mirror plane from 0.46 to 0.98 of the dome, and terminator steps from 2.1% to 0.45%, at no measurable cost in render time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MaterialX BSDF graphs.&lt;/strong&gt; The teapot and lion use standalone BSDF nodes (conductor, dielectric, Oren–Nayar, sheen) combined by layer and mix. The renderer now flattens these graphs onto its layered BSDF. Value graphs that feed them (multiply, power, ln and exp, normal maps and so on) become texture expressions: constants fold, single images stay images, and everything else is baked per UDIM tile when textures load. A layer map carries coat, sheen and specular weights that vary across the surface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A mesh bug found along the way.&lt;/strong&gt; The lion's wooden base stayed cream even with its raw texture bound. The cause was in mesh loading: for meshes made only of triangles, Hydra reports that triangulation left face-varying data unchanged, and that answer was treated as a failure, so such meshes lost their UVs. Pre-triangulated exports are common, so this affected far more than one asset.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matching the official lighting.&lt;/strong&gt; Tone curves turned out to matter little. A guessed bright studio scored worse than the built-in sky because its white floor whitened the glaze. Instead, a six-parameter studio (key light direction, size and strength, floor albedo, dome warmth) was fitted to both official images at once: about 200 renders in one process, about 3.5 minutes. Turing Sim's Compare Renders gained a matching studio override.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gold that leaned yellow.&lt;/strong&gt; An isolated sphere showed the conductor itself was right. The problem was mixing a metal and a diffuse base under a soft mask: one blended colour paled the gold, and specular weight scaled the metal a second time. Each lobe now averages its parameters over its own share, and the metal keeps its own colour.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;New headless tests cover each fix, and each was checked to fail without its fix. The renderer moved to its own TuringRender repository, Turing Sim installs it in editable mode, and all branches were merged after full test runs (TuringRender 45 tests, Turing Sim 520 passed after the final merge).&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;Against the official renders, measured on object pixels as mean sRGB difference out of 255 at the best exposure: the teapot went from 44.5 (grey fallback) to 29.4 under the built-in sky and 24.1 under the fitted studio; the lion from 51.5 to 40.1 and 20.2.&lt;/p&gt;

&lt;p&gt;Limits remain. The glaze's view-dependent absorption is evaluated head-on, so the teapot's blue is lighter and the lion's gold less orange than the official images. Coat and base share one normal. Diffuse surfaces still lose some energy under strong normal maps. Implicit USD spheres are still tessellated coarsely. The next step is view-dependent absorption, which should deepen both the blue glaze and the gold.&lt;/p&gt;




&lt;h2&gt;
  
  
  背景
&lt;/h2&gt;

&lt;p&gt;Turing RT 是机器人仿真器 Turing Sim 背后的 Metal 路径追踪渲染器，已经有 OpenPBR 风格的分层材质、HDRI 重要性采样和 AI 降噪。今天的问题很直接：为什么渲染出来还是不好看？&lt;/p&gt;

&lt;p&gt;第一个结论落在场景内容上，不在渲染器。机器人工作站场景里没有灯，也没有材质，只有显示颜色，所以路径追踪和实时光追渲出来几乎一样：既没有可以反弹的间接光，也没有可以反射的光滑表面。在同一个机器人场景里加上地面、两盏面光和几种真实材质，软阴影和光泽反射马上就出来了。于是工作转向渲染器本身真正出错的地方。&lt;/p&gt;

&lt;h2&gt;
  
  
  目标与难点
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;让玻璃和金属在真实资产上站得住，先从一个材质测试场景开始；&lt;/li&gt;
&lt;li&gt;支持 ASWF 数字制作示例库里 NVIDIA 的 MaterialX Teapot 和 Lion，它们原来完全显示为灰色；&lt;/li&gt;
&lt;li&gt;解释它们和官方渲染图的色差，并把能测量的差距尽量缩小；&lt;/li&gt;
&lt;li&gt;让 Turing Sim 始终用上最新的渲染器。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;全程的约束是项目规则：每一处渲染改动都要附带与参考图的测量对比。&lt;/p&gt;

&lt;h2&gt;
  
  
  解决思路与行动
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;低模平滑网格的黑边。&lt;/strong&gt; 金属球边缘出现黑圈。换成 256×128 的细网格后黑圈消失，说明问题出在粗面片上插值出来的法线：轮廓被误判成背面，反射射线钻进了表面。修复包括三点：用几何面判断正反面；保证不透明表面的着色法线有效（Cycles 的做法，二次方程的选根用 20 万个随机样本做了数值验证）；明暗交界线附近的阴影射线改从平滑曲面上发出（Hanika 2021）。白炉测试中，12 段镜面球的偏暗像素从 3.3% 降到 0，强法线贴图的镜面平面从环境亮度的 0.46 倍升到 0.98 倍，交界线锯齿从 2.1% 降到 0.45%，渲染耗时没有可测量的增加。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MaterialX BSDF 图。&lt;/strong&gt; Teapot 和 Lion 的材质由独立的 BSDF 节点（conductor、dielectric、Oren–Nayar、sheen）通过 layer 和 mix 组合而成。渲染器现在把整张图压平到自己的分层 BSDF 上。驱动它们的数值节点链（multiply、power、ln/exp、法线贴图等）变成纹理表达式：常数直接折叠，单张图仍按贴图读取，其余的在加载贴图时按 UDIM 瓦片烘焙。另外用一张 layer 贴图承载随位置变化的清漆、绒毛和高光权重。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;顺带发现的网格 bug。&lt;/strong&gt; 即使直接绑上原始贴图，Lion 的木底座仍是米白色。原因在网格加载：对于全是三角形的网格，Hydra 会告诉调用方 faceVarying 数据"无需改变"，而这个结果被当成了失败，于是这类网格丢掉了 UV。预先三角化的导出很常见，影响面远不止这一个资产。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;匹配官方灯光。&lt;/strong&gt; 实测发现色调曲线影响很小。手搭的亮白影棚反而比内置天空更差，因为白地面把釉面映白了。最后用 6 个参数的影棚（主光方向、大小和强度，地面反照率，环境光冷暖）同时拟合两张官方图：在一个进程里渲约 200 次，用时约 3.5 分钟。Turing Sim 的 Compare Renders 也加上了对应的影棚覆盖项。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;偏黄的金色。&lt;/strong&gt; 单独渲一个金属球，证明导体本身是对的。问题出在软遮罩下混合金属和漫反射：混成一个颜色会把金色冲淡，高光权重还会把金属再缩放一次。现在每个波瓣只在自己所占的份额里平均参数，金属保留自己的颜色。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;每处修复都有新的无窗口测试，并确认去掉修复后测试会失败。渲染器搬进了独立的 TuringRender 仓库，Turing Sim 以 editable 方式安装它；所有分支在完整测试后合并（TuringRender 45 个测试；最后一次合并后 Turing Sim 520 个通过）。&lt;/p&gt;

&lt;h2&gt;
  
  
  结果与下一步
&lt;/h2&gt;

&lt;p&gt;与官方渲染图对比（只看物体像素，在最佳曝光下计算 sRGB 平均差，满分 255）：Teapot 从 44.5（灰色回退）降到内置天空下的 29.4、拟合影棚下的 24.1；Lion 从 51.5 降到 40.1 和 20.2。&lt;/p&gt;

&lt;p&gt;仍有局限：釉的视角相关吸收按正对视角计算，所以 Teapot 的蓝比官方浅，Lion 的金不如官方橙；清漆层和底层共用一个法线；强法线贴图下漫反射仍会损失一些能量；USD 隐式球体的细分仍然偏粗。下一步是视角相关吸收，它会让蓝釉和金色都更深。&lt;/p&gt;

</description>
      <category>design</category>
      <category>gamedev</category>
      <category>software</category>
    </item>
    <item>
      <title>Turing Sim: Contact Forces You Can Actually Read</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Sat, 17 Oct 2026 02:49:50 +0000</pubDate>
      <link>https://dev.to/alphanine/turing-sim-contact-forces-you-can-actually-read-2o90</link>
      <guid>https://dev.to/alphanine/turing-sim-contact-forces-you-can-actually-read-2o90</guid>
      <description>&lt;p&gt;Each touching pair now gets one named arrow: who pushes whom, and how hard.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;中文版在文末，点开即可阅读。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb7ldxef398taqqte8stt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb7ldxef398taqqte8stt.png" alt="Contact forces on the held cube, before and after · Before" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpnl9jlo4rdmgqs5cqic4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpnl9jlo4rdmgqs5cqic4.png" alt="Contact forces on the held cube, before and after" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsqmmeq2ae95mjd9ng71v.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsqmmeq2ae95mjd9ng71v.gif" alt="Contact forces on the held cube, before and after · GIF" width="759" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Before: one unlabelled peak reading. After: the two pads each pressing 21.2 N on the cube.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Famerj26jn1n94pl1gnwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Famerj26jn1n94pl1gnwh.png" alt="Contact summary in the Robot panel, before and after · Before" width="596" height="1452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy2n0kmwodms52vmcj3c9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy2n0kmwodms52vmcj3c9.png" alt="Contact summary in the Robot panel, before and after" width="596" height="1452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The panel lists named pairs and says how to see every push on a body.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;Turing Sim can draw contact forces while physics runs. Before this change every contact point got its own arrow, and the only label was the strongest force. Holding a cube with a two-finger gripper produced a yellow cluster with "2.78 N" next to it. You could not tell which finger pushed, how hard each side squeezed, or whether a robot's own links touching each other were drowning out the contact you cared about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;p&gt;Make the overlay answer the questions people actually ask when a grasp misbehaves: who is pushing on this body, in which direction, and how hard? It also had to stay readable on a hand with many links, and work the same in the Storm and the native Metal viewports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One arrow per touching pair.&lt;/strong&gt; Contact points are grouped by the two bodies involved. Each pair draws one net-push arrow ending at the force-weighted centre of its points; small dots still mark the individual points.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The selection decides what is shown.&lt;/strong&gt; With nothing selected, the strongest pairs are labelled "left pad → Cube · 14.5 N". Select a body and every push on it is drawn, each oriented onto that body.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal contacts are separated.&lt;/strong&gt; Contacts between parts of one articulation (two fingers of one hand, the pieces of a puzzle cube) are hidden unless "Include self-contacts" is on, so they never crowd out the rest. MuJoCo and Genesis both report which tree each body belongs to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Details on hover, and glows.&lt;/strong&gt; Hovering an arrow opens a card with the pressing and friction components. The native Metal view now forwards pointer motion so this works in every viewport mode. An optional glow marks each point, cool to hot with its force.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The commit was tested in isolation: 88 tests passed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;The captures show the same grasp before and after. The old view had one unlabelled reading; the new one shows the right and left pads each pressing 21.2 N on the cube and a follower link touching it at 2.42 N. The Robot panel lists the same pairs and points to selection for the full picture.&lt;/p&gt;

&lt;p&gt;This is a debugging aid, not a measurement tool: the numbers are whatever the engine reports, and the two engines still differ.&lt;/p&gt;




&lt;p&gt;&lt;/p&gt;
  中文版 · Chinese version（点击展开）
  &lt;p&gt;每对接触的物体现在只有一个带名字的箭头：谁推谁、推多大。&lt;/p&gt;
&lt;h2&gt;
  
  
  背景
&lt;/h2&gt;

&lt;p&gt;Turing Sim 在物理运行时可以显示接触力。改动前每个接触点各有一个箭头，唯一的标签是最大的那个力。用两指夹爪夹住方块时，只能看到一团黄色和旁边的 "2.78 N"。看不出是哪根手指在推、两边各夹多紧，也分不清机器人自身连杆之间的接触是不是把真正关心的接触淹没了。&lt;/p&gt;
&lt;h2&gt;
  
  
  目标与难点
&lt;/h2&gt;

&lt;p&gt;让叠加层回答抓取出问题时最常问的几个问题：谁在推这个物体、往哪个方向、推多大。同时在多连杆的手上也要看得清，并且在 Storm 和原生 Metal 视口里表现一致。&lt;/p&gt;
&lt;h2&gt;
  
  
  解决思路与行动
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;每对接触物体一个箭头。&lt;/strong&gt; 接触点按涉及的两个物体分组。每对画一个合力箭头，终点在按力加权的接触点中心，小圆点仍标出各个接触点。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;由选中对象决定显示什么。&lt;/strong&gt; 什么都不选时，标出最强的几对，比如 "left pad → Cube · 14.5 N"。选中一个物体，就显示所有作用在它上面的推力，方向都朝向这个物体。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;内部接触单独处理。&lt;/strong&gt; 同一个关节体内部的接触（同一只手的两根手指、魔方的块）默认隐藏，勾选 "Include self-contacts" 才显示，不会挤掉其它接触。MuJoCo 和 Genesis 都会报告每个物体属于哪棵运动树。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;悬停查看细节，可选发光。&lt;/strong&gt; 鼠标悬停在箭头上会弹出卡片，显示法向力和摩擦力分量。原生 Metal 视口现在会转发鼠标移动，所以每种视口模式下都能用。可选的发光效果按力的大小由冷到热标出每个接触点。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这个 commit 单独测试：88 项通过。&lt;/p&gt;
&lt;h2&gt;
  
  
  结果与下一步
&lt;/h2&gt;

&lt;p&gt;截图对比的是同一次抓取。旧视图只有一个没有标注的读数；新视图显示左右夹爪各以 21.2 N 压住方块，另一个随动连杆以 2.42 N 接触。Robot 面板列出同样的接触对，并提示选中物体可以看到全部推力。&lt;/p&gt;

&lt;p&gt;这是调试辅助，不是测量工具：数值就是引擎报告的结果，两个引擎之间仍有差异。&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>gamedev</category>
      <category>programming</category>
      <category>robotics</category>
    </item>
    <item>
      <title>Turing Sim: Seeing Sensors in the Viewport and the Robot Panel</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Sat, 17 Oct 2026 02:43:05 +0000</pubDate>
      <link>https://dev.to/alphanine/turing-sim-seeing-sensors-in-the-viewport-and-the-robot-panel-5gh</link>
      <guid>https://dev.to/alphanine/turing-sim-seeing-sensors-in-the-viewport-and-the-robot-panel-5gh</guid>
      <description>&lt;p&gt;Sensors now show where they are in the viewport, and the Robot panel shows what they measure.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;中文版在文末，点开即可阅读。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9erfx86fc5a8ld99iyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9erfx86fc5a8ld99iyw.png" alt="The scene while editing, before and after · Before" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F47wrp2w2hskzelepucqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F47wrp2w2hskzelepucqk.png" alt="The scene while editing, before and after" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;After: camera frustums, the LiDAR head and the IMU axes are drawn where the sensors are.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj6uamx2h7z9xhi9hvh74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj6uamx2h7z9xhi9hvh74.png" alt="The Robot panel while playing, before and after · Before" width="596" height="1452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fshbczzhul52wona635q2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fshbczzhul52wona635q2.png" alt="The Robot panel while playing, before and after" width="596" height="1452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;After: live sensor rows, LiDAR colouring with a legend, IMU charts and the camera preview.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh0oug2kesntedoo7dgf2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh0oug2kesntedoo7dgf2.png" alt="LiDAR coloured by distance" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The scan with a distance ramp and its range in the legend.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;With IMUs, LiDARs and cameras simulated, the editor had to show them. The first version listed sensors as plain text and drew LiDAR points in eight shades of one colour. You could not see where a camera pointed until you pressed Play. During the session the user asked for better visualization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;p&gt;Show each sensor where it is, whether it is live, and what it measures, in every viewport mode and in a panel that fits a narrow inspector.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sensors in the viewport.&lt;/strong&gt; Camera frustums with a tab marking image up, LiDAR heads with their field of view, and IMU axes in the viewport's X, Y, Z colours. While simulating, each IMU also draws its measured specific force. These gizmos show while editing too, which makes placing a sensor easy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A panel that reads at a glance.&lt;/strong&gt; One row per sensor with its kind, mount and status (Ready, Live, Note). LiDAR scans can be coloured by intensity, distance, height or class with a Turbo legend. IMUs get four seconds of acceleration and angular velocity per axis. Cameras get a live preview of any output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One recording.&lt;/strong&gt; Record sensors writes the host's IMU and LiDAR data and the camera worker's frames into one folder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixes found by capturing the real app.&lt;/strong&gt; The native captures for this post showed three layout problems, each fixed with a regression test. The preset picker had widened the Robot panel from 322 to 402 pt and scrolled it sideways. Stale rows painted "Ready" under "Live". Six camera outputs were elided in a segmented control and now use a full-width picker.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;The before/after captures run the same scene. Before, the viewport shows nothing where the sensors are and the panel has no sensor section. After, frustums, the LiDAR head and the IMU axes appear while editing. While playing, the panel shows live rows, the coloured scan with its legend, the IMU charts and the camera. The panel is back to its pre-sensor width.&lt;/p&gt;




&lt;p&gt;&lt;/p&gt;
  中文版 · Chinese version（点击展开）
  &lt;p&gt;视口里能看到传感器在哪里，Robot 面板里能看到它们测到了什么。&lt;/p&gt;
&lt;h2&gt;
  
  
  背景
&lt;/h2&gt;

&lt;p&gt;IMU、LiDAR 和相机都能仿真之后，编辑器得把它们展示出来。第一版用纯文本列出传感器，LiDAR 点只用同一种颜色的八个深浅来画。按下 Play 之前，你看不出相机朝哪儿拍。会话中用户也提出可视化需要更好一些。&lt;/p&gt;
&lt;h2&gt;
  
  
  目标与难点
&lt;/h2&gt;

&lt;p&gt;在每种视口模式下显示每个传感器的位置、是否在工作、测到了什么；面板要能放进很窄的检查器里。&lt;/p&gt;
&lt;h2&gt;
  
  
  解决思路与行动
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;视口里画出传感器。&lt;/strong&gt; 相机画成视锥，用一个小标记指示画面的上方；LiDAR 画出探头和视场；IMU 画出坐标轴，颜色和视口的 X、Y、Z 轴一致。仿真时每个 IMU 还会画出它测到的比力。编辑时这些也会显示，方便摆放传感器。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;一眼能看懂的面板。&lt;/strong&gt; 每个传感器一行，显示类型、挂载位置和状态（Ready、Live、Note）。LiDAR 扫描可以按强度、距离、高度或类别上色，并带 Turbo 色带图例。IMU 显示最近 4 秒每个轴的加速度和角速度。相机可以实时预览任意一种输出。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;一次录制。&lt;/strong&gt; Record sensors 把宿主的 IMU、LiDAR 数据和相机 worker 的帧写进同一个文件夹。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;在真实 app 里截图时发现并修复的问题。&lt;/strong&gt; 为这篇文章截原生窗口时发现了三个布局问题，都已修复并加了回归测试。一是预设下拉框把 Robot 面板从 322 撑到 402，导致横向滚动。二是旧行没有及时移除，"Ready" 和 "Live" 两个状态标签叠在一起。三是六个相机输出在分段控件里被省略显示，现在改成全宽下拉框。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  结果与下一步
&lt;/h2&gt;

&lt;p&gt;改前改后用的是同一个场景。改前，视口里传感器的位置什么都没有，面板也没有传感器区。改后，编辑时就能看到视锥、LiDAR 探头和 IMU 坐标轴。播放时，面板显示实时状态行、上色的点云和图例、IMU 曲线以及相机画面。面板也恢复到了加传感器之前的宽度。&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

</description>
      <category>robotics</category>
      <category>showdev</category>
      <category>tools</category>
    </item>
    <item>
      <title>Turing Sim: Making the USD Schemas Inspector Readable</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Tue, 29 Sep 2026 04:31:32 +0000</pubDate>
      <link>https://dev.to/alphanine/turing-sim-making-the-usd-schemas-inspector-readable-57h3</link>
      <guid>https://dev.to/alphanine/turing-sim-making-the-usd-schemas-inspector-readable-57h3</guid>
      <description>&lt;p&gt;The running editor shows separate USD schema cards, UR10 articulation settings and the source asset window.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;中文版在文末，点开即可阅读。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6rih43ovd4582zdw40qx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6rih43ovd4582zdw40qx.png" alt="Schema cards in the running editor" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Each USD schema on the selected cube (Collision, Mesh Collision, Rigid Body, Mass) now sits in its own card with its USD schema name.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4pb1subhsai6z9stqq52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4pb1subhsai6z9stqq52.png" alt="UR10 Transform and Articulation Root in the native editor" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The selected UR10 shows Translate, Rotate, Scale and Articulation Root settings in the running editor.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp9cvmwiynqwefsbpy0cb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp9cvmwiynqwefsbpy0cb.png" alt="Source asset editor in the native app" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The source asset window shows a hierarchy, preview and Properties/Raw USD tabs.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;Turing Sim is an experimental OpenUSD editor and robotics simulation workbench. Its Schemas tab lists every schema on the selected prim, including physics APIs, and every edit goes through the document's undo history. On September 28 the tab worked, but it was hard to read. A viewport tool section sat between the name and the Transform, schemas without properties printed "No additional properties", and the Rigid Body and Mass fields ran together with no visible boundary. Transform showed only the transform parts already stored in the file, so the UR10 showed Position but no Rotation or Scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;p&gt;The goal was to make the tab read the way USD is structured, following the conventions of Isaac Sim's Property panel. Every property should clearly belong to one schema, Transform should own translate, rotation and scale, and the Articulation Root should expose settings such as self collision. The difficulty was doing this without hardcoding robot or schema names, which the project rules forbid, and without breaking undo.&lt;/p&gt;

&lt;p&gt;A second problem appeared during testing. After moving the UR10 with the gizmo, the Transform section switched to "custom transform stack" and the position fields went stale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One card per schema.&lt;/strong&gt; Each schema is now a bordered card. Its header shows a readable name on the left, the USD schema identifier (for example &lt;code&gt;PhysicsRigidBodyAPI&lt;/code&gt;) on the right, and a remove button for applied APIs. Removing a schema also clears its values in the current edit layer, as one undo step. Cards that are mostly arrays, such as mesh topology, start collapsed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isaac Sim-style rows.&lt;/strong&gt; Rows are two columns, label and value, and stack when the panel is narrow. Labels come from the schema's own &lt;code&gt;displayName&lt;/code&gt; metadata, such as "Center of Mass". A dot at the end of each row shows whether the value is authored: hollow for the schema default, blue when set in the current edit layer (click to revert), grey when set in another layer. Booleans are checkboxes, and physics fields whose USD default is infinity read as "Auto".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transform owns position, rotation and scale.&lt;/strong&gt; Translate, Rotate (or Orient) and Scale always appear, named after their USD transform ops. Rotation switches between Euler degrees and a quaternion. Either input is converted to whichever form the prim already stores, so Isaac-style &lt;code&gt;orient&lt;/code&gt; quaternions stay quaternions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The gizmo edits the real transform.&lt;/strong&gt; The cause of the stale fields was that drags wrote a separate matrix op instead of changing the prim's own translate and rotate values. Drags now update those values directly. The matrix op remains only as a fallback for transforms that position/rotation/scale cannot represent. Older matrix ops are merged back on the next edit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Articulation Root settings.&lt;/strong&gt; The card counts the rigid bodies and joints found under the root and offers the PhysX articulation settings: enabled, self collisions, solver iterations, and sleep and stabilization thresholds. Editing one writes the value and applies the PhysX articulation schema in the same undo step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source asset editor.&lt;/strong&gt; The separate editor for referenced assets now uses the same glass cards and segmented tabs as the main window. A user-provided screenshot of the native editor shows the revised layout; editing behavior in that window has not been checked in a headed session.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Regression tests cover each new edit path with undo and redo, plus a check of all six Euler axis orders against USD's own rotation ops. The full offscreen suite gave 525 passed, 3 failed and 6 skipped. The three failures were older tests still looking up the field by its previous name, "Position X". After updating them, the affected test files pass (61 tests). The whole suite has not been re-run since that fix. The three screenshots at the top show the running app after the changes: separate schema cards, the UR10 Transform and Articulation Root panels, and the source asset editor. They show visible layout and values, not an end-to-end edit or save. The other checks were headless, and today's changes are not yet committed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;In the running editor, a cube with collision, rigid body and mass now shows four clearly separated cards, and each field sits under the schema that owns it. Gizmo moves now show up in the Transform fields, and the UR10's articulation settings can be edited in place.&lt;/p&gt;

&lt;p&gt;Next steps are to commit and push these changes, re-run the full suite, test the gizmo against real robot assets in the native viewport, and check the Translate fields at narrow panel widths.&lt;/p&gt;




&lt;p&gt;&lt;/p&gt;
  中文版 · Chinese version（点击展开）
  &lt;p&gt;运行中的编辑器展示分开的 USD schema 卡片、UR10 关节体设置及源资产窗口。&lt;/p&gt;
&lt;h2&gt;
  
  
  背景
&lt;/h2&gt;

&lt;p&gt;Turing Sim 是一个实验性的 OpenUSD 编辑器和机器人仿真工作台。Schemas 页会列出所选 prim 上的所有 schema（包括物理 API），所有修改都进入文档的撤销历史。9 月 28 日时，这个页面能用，但很难读：视口工具区夹在名称和 Transform 之间；没有属性的 schema 显示“No additional properties”；刚体（Rigid Body）和质量（Mass）的字段之间没有边界，混在一起。Transform 只显示文件里已写入的部分，所以 UR10 只有位置，没有旋转和缩放。&lt;/p&gt;
&lt;h2&gt;
  
  
  目标与难点
&lt;/h2&gt;

&lt;p&gt;目标是让这个页面按 USD 的结构来呈现，并参考 Isaac Sim 属性面板的做法：每个属性都明确属于一个 schema；Transform 负责位置、旋转和缩放；Articulation Root 能设置自碰撞等参数。难点在于不能硬编码机器人或 schema 名称（项目规则禁止），也不能破坏撤销。&lt;/p&gt;

&lt;p&gt;测试时又发现一个问题：用 gizmo 移动 UR10 后，Transform 变成“custom transform stack”，位置数值不再更新。&lt;/p&gt;
&lt;h2&gt;
  
  
  解决思路与行动
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;每个 schema 一张卡片。&lt;/strong&gt; 每个 schema 现在是一张带边框的卡片。标题栏左边是易读的名称，右边是 USD schema 标识（例如 &lt;code&gt;PhysicsRigidBodyAPI&lt;/code&gt;），已应用的 API 还有删除按钮。删除 schema 时会一并清除它在当前编辑层的数值，整体是一步撤销。以数组为主的卡片（如网格拓扑）默认折叠。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isaac Sim 风格的属性行。&lt;/strong&gt; 每行左边是标签、右边是数值，面板较窄时自动上下排列。标签来自 schema 自带的 &lt;code&gt;displayName&lt;/code&gt; 元数据，例如“Center of Mass”。行尾的圆点表示数值是否已写入：空心是 schema 默认值；蓝色表示已写入当前编辑层（点击可恢复默认）；灰色表示写在其他层。布尔值改为复选框；USD 默认值为无穷大的物理字段显示为“Auto”。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transform 负责位置、旋转和缩放。&lt;/strong&gt; Translate、Rotate（或 Orient）和 Scale 始终显示，名称与 USD 变换操作一致。旋转可在欧拉角和四元数之间切换，任一种输入都会转换成 prim 原本使用的形式，因此 Isaac 风格的 &lt;code&gt;orient&lt;/code&gt; 四元数仍保持为四元数。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gizmo 直接修改真实变换。&lt;/strong&gt; 数值不更新的原因是：拖动时写入的是一个额外的矩阵操作，而不是修改 prim 自身的平移和旋转值。现在拖动直接更新这些值。只有位置/旋转/缩放无法表示的变换才保留矩阵操作作为后备；旧的矩阵操作会在下次编辑时合并回去。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Articulation Root 设置。&lt;/strong&gt; 卡片会统计根节点下找到的刚体和关节数量，并提供 PhysX 关节体设置：启用、自碰撞、求解器迭代次数、休眠阈值和稳定阈值。修改其中一项时，会在同一步撤销里写入数值并应用 PhysX 关节体 schema。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;源资产编辑器。&lt;/strong&gt; 引用资产的独立编辑器改用与主窗口相同的玻璃卡片和分段标签。用户提供的原生编辑器截图展示了新版布局；该窗口中的编辑操作尚未经过有界面验证。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;每条新的编辑路径都有带撤销/重做的回归测试，另外还用 USD 自身的旋转操作验证了全部六种欧拉轴顺序。完整的离屏测试结果为 525 项通过、3 项失败、6 项跳过。3 项失败都是旧测试仍按原名称“Position X”查找字段；更新后，相关测试文件（61 项）全部通过，但此后尚未重新运行整套测试。文章顶部的三张截图均展示改动后运行中的应用：分开的 schema 卡片、UR10 的 Transform 与 Articulation Root 面板，以及源资产编辑器。它们证明的是可见布局与数值，并不能证明完整的编辑或保存流程。其余检查均为无界面运行，今天的改动也还没有提交。&lt;/p&gt;
&lt;h2&gt;
  
  
  结果与下一步
&lt;/h2&gt;

&lt;p&gt;在运行中的编辑器里，一个带碰撞、刚体和质量的立方体现在显示为四张清晰分开的卡片，每个字段都在它所属的 schema 下面。gizmo 的移动会直接反映在 Transform 字段里，UR10 的关节体设置也可以直接编辑。&lt;/p&gt;

&lt;p&gt;下一步：提交并推送这些改动，重新运行整套测试，在原生视口中用真实机器人资产测试 gizmo，并检查窄面板下的 Translate 字段。&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

</description>
      <category>software</category>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
      <category>tools</category>
    </item>
    <item>
      <title>From Localhost to a Built-In Tab: Keeping Story Engine Beside the Conversation</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Mon, 28 Sep 2026 05:31:04 +0000</pubDate>
      <link>https://dev.to/alphanine/from-localhost-to-a-built-in-tab-keeping-story-engine-beside-the-conversation-3gd</link>
      <guid>https://dev.to/alphanine/from-localhost-to-a-built-in-tab-keeping-story-engine-beside-the-conversation-3gd</guid>
      <description>&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;Story Engine began with a review page opened through a local website. It provided a place to browse drafts, read an article and edit it before publication. The project conversation lived elsewhere.&lt;/p&gt;

&lt;p&gt;Embedding the entire review page directly in the conversation introduced another layout problem. A long article could occupy most of the chat, making it awkward to move between discussing a change and reviewing the draft.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;p&gt;The goal was to give Story Engine its own review Tab beside the conversation, while preserving the existing interface and local drafts. The chat needed a small entrance; the article needed enough room to read.&lt;/p&gt;

&lt;p&gt;This also required a clear boundary between the application and its host. Story Engine could request a presentation mode, while the client controlled where and how that workspace appeared.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;p&gt;The implementation packages the existing page, styles, scripts and brand assets as an MCP UI resource. In the hosted interface, actions go through the same business service used by the browser version. This MCP route serves the interface without starting a localhost HTTP listener and continues using the local data store.&lt;/p&gt;

&lt;p&gt;On initialization, the entrance requests the host's expanded presentation once. In this client, that presentation provides the built-in Tab. The inline conversation keeps a compact launcher, with an “Open Tab” button available if the automatic request does not switch the view. Closing the Tab does not start an automatic reopening loop.&lt;/p&gt;

&lt;p&gt;The move also exposed a resource-version problem. A newer page could otherwise reach an older process whose registered tools had not changed. Each new server process now retains its bundled UI snapshot alongside its tool registrations. Loading newly added tools still requires the client to reload the updated server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result and next steps
&lt;/h2&gt;

&lt;p&gt;The accompanying user-provided screenshot shows Story Engine's draft library in a named Tab on the right, with the project conversation still visible on the left. It provides direct evidence of the opened workspace in this client. The opening transition itself was not recorded, so the image does not establish whether that particular Tab opened automatically.&lt;/p&gt;

&lt;p&gt;Source inspection and simulated-host tests cover the compact entrance, the presentation request, fallback behavior and shared operations. The screenshot complements those checks by showing the actual host layout. It does not establish that every review action has been exercised inside that Tab.&lt;/p&gt;

&lt;p&gt;The practical improvement is that the conversation and draft workspace can remain visible together. Story Engine still runs locally on the user's Mac. The next step is to verify the complete opening, review and return journey across supported clients and a clean installation, including the behavior of older running plugin processes.&lt;/p&gt;




&lt;h2&gt;
  
  
  从 localhost 到内置 Tab：让 Story Engine 审稿区留在对话旁
&lt;/h2&gt;

&lt;h3&gt;
  
  
  背景
&lt;/h3&gt;

&lt;p&gt;Story Engine 最初通过本地网站打开审稿页，用户可以在里面浏览草稿、阅读文章，并在发布前编辑。项目讨论则留在另一处对话中。&lt;/p&gt;

&lt;p&gt;后来把完整审稿页嵌进对话，又产生了布局问题：长文章会占据大部分聊天空间，在讨论修改和阅读草稿之间切换变得不够顺手。&lt;/p&gt;

&lt;h3&gt;
  
  
  目标与难点
&lt;/h3&gt;

&lt;p&gt;目标是让 Story Engine 在对话旁拥有独立的审稿 Tab，同时保留已有界面和本地草稿。对话只需要一个紧凑入口，文章则需要足够的阅读空间。&lt;/p&gt;

&lt;p&gt;应用与宿主之间的职责也需要明确：Story Engine 可以请求一种呈现方式，工作区最终放在哪里、怎样出现，由客户端决定。&lt;/p&gt;

&lt;h3&gt;
  
  
  解决思路与行动
&lt;/h3&gt;

&lt;p&gt;当前实现把原有页面、样式、脚本和品牌素材打包为 MCP UI 资源。在宿主内打开时，界面操作通过与浏览器版相同的业务服务处理。这条 MCP 路径不需要启动 localhost HTTP 监听，仍然使用原来的本地数据存储。&lt;/p&gt;

&lt;p&gt;入口初始化后，会请求一次宿主的展开呈现方式；在这个客户端中，它对应内置 Tab。对话内保留紧凑的启动入口，如果自动请求没有切换视图，用户仍可点击“打开 Tab”。关闭 Tab 后，也不会进入反复自动打开的循环。&lt;/p&gt;

&lt;p&gt;这次迁移还暴露了资源版本问题：新页面可能连接到仍注册着旧工具的进程。现在，每个新启动的服务进程都会固定保存与自身工具注册配套的界面快照。新增工具要真正进入当前客户端，仍需重新加载更新后的服务。&lt;/p&gt;

&lt;h3&gt;
  
  
  结果与下一步
&lt;/h3&gt;

&lt;p&gt;附图由用户提供，真实显示了右侧名为 Story Engine 的 Tab 和草稿列表，左侧项目对话仍然可见。这直接证明了该客户端中已经打开的工作区。打开过程没有录制，因此这张静态截图不能确定当时是否自动切换到了 Tab。&lt;/p&gt;

&lt;p&gt;源码核对和模拟宿主测试覆盖了紧凑入口、呈现请求、回退行为，以及共用的业务操作。真实截图补充了实际宿主布局的证据，但不能据此认定所有审稿操作都已在这个 Tab 中逐项验证。&lt;/p&gt;

&lt;p&gt;实际变化是：项目讨论和草稿工作区可以同时留在视野内。Story Engine 仍在用户的 Mac 上本地运行。下一步需要在支持的客户端及干净安装环境中，验证打开、审稿和返回对话的完整路径，并继续检查旧插件进程的兼容行为。&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>frontend</category>
      <category>software</category>
      <category>ui</category>
    </item>
    <item>
      <title>Turing Sim: Turning a UI Refresh into a Shareable Milestone</title>
      <dc:creator>alphanine</dc:creator>
      <pubDate>Mon, 28 Sep 2026 05:15:31 +0000</pubDate>
      <link>https://dev.to/alphanine/turing-sim-turning-a-ui-refresh-into-a-shareable-milestone-p2l</link>
      <guid>https://dev.to/alphanine/turing-sim-turning-a-ui-refresh-into-a-shareable-milestone-p2l</guid>
      <description>&lt;h1&gt;
  
  
  Turing Sim: Turning a UI Refresh into a Shareable Milestone
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;中文版在文末，点开即可阅读。&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;Turing Sim is an experimental OpenUSD editor and robotics simulation workbench. Its interface needs to keep the 3D viewport central while giving scene editing, inspection, and simulation controls a coherent place to live. The project also has a strict rule for interactive USD edits: they must enter the document command history so users can undo and redo them.&lt;/p&gt;

&lt;p&gt;The project owner reported a much better interface after work with Claude Code. On September 27, the immediate need was to capture that work in a reviewable Git commit and make it available on GitHub. The commit includes more than visual changes, so this is a record of the milestone rather than a claim that every feature was built that day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task
&lt;/h2&gt;

&lt;p&gt;The goal was to preserve the current application work without sweeping generated output into source control, check what could be verified headlessly, and synchronize it with the published repository. A second challenge was communicating the limits of that verification: the local Python environment does not include every binding used by the full desktop editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;p&gt;The recorded changes establish a shared Qt design system with appearance modes, reusable controls, icons, and glass surfaces. The accompanying screenshot shows the newer glass treatment across the window chrome, dock tabs, panels, and viewport tools. The UI work also spans dock tabs, workspace layout, editor chrome, viewport controls, and robot panels. Separately, the commit adds a common runtime contract for Genesis and MuJoCo, a MuJoCo conversion path, and physics authoring through the undoable document command flow. These are distinct areas of work collected in one milestone commit.&lt;/p&gt;

&lt;p&gt;The full headless test command completed with 372 passing tests and 14 skips. It also reported 12 failures and 3 errors. Several editor tests could not import &lt;code&gt;pxr.Usdviewq&lt;/code&gt;; native schema subprocess tests crashed in the local macOS/OpenUSD environment. Those outcomes prevent a clean-suite claim and call for validation in the intended runtime. The generated MuJoCo warning log was left out of the commit.&lt;/p&gt;

&lt;p&gt;GitHub authentication was refreshed. The remote repository had separate commit history but the same pre-change file tree, so the new commit was replayed onto the remote &lt;code&gt;main&lt;/code&gt; and pushed without replacing its existing history. The resulting commit is &lt;a href="https://github.com/lydd8888/TuringSim/commit/5881d6fbef146b244dbbc457cd9d90bcafa3cd45" rel="noopener noreferrer"&gt;&lt;code&gt;5881d6f&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before and after: the interface
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjevzrl1w8qlp3fsoo85c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjevzrl1w8qlp3fsoo85c.png" alt="Earliest available Turing Sim interface, before the glass redesign" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Earlier interface: flat charcoal panels and underlined tabs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq92cq3urakz8mc0dsomu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq92cq3urakz8mc0dsomu.png" alt="Current Turing Sim glass interface with scene tree, viewport, and inspector" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Current interface: glass surfaces and grouped controls around the viewport.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The earliest UI capture available for this comparison shows a flatter, mostly charcoal workspace: underlined Objects/Layers tabs, a fixed Inspector, and a small floating tool strip. The current capture shows blue-tinted glass around the viewport, capsule-shaped dock tabs and toolbar groups, a searchable scene tree, and more prominent floating viewport controls. The 3D canvas remains the visual center in both.&lt;/p&gt;

&lt;p&gt;These screenshots use different selection and camera states. They illustrate changes in layout and visual language, not a controlled rendering-quality or performance comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;The UI and simulation work is now recorded and available for review on GitHub. The design system documentation gives future panels a consistent visual vocabulary, while the engine adapter documentation lays out a path for comparable runtime checks. It explicitly treats benchmark numbers as unmeasured until the benchmark scripts are run and recorded.&lt;/p&gt;

&lt;p&gt;The next validation step is to run the editor and schema checks in the supported macOS launch environment, investigate the observed failures, and record measured engine results before drawing performance conclusions.&lt;/p&gt;




&lt;p&gt;&lt;/p&gt;
  中文版 · Chinese version（点击展开）
  &lt;h1&gt;
  
  
  Turing Sim：将界面更新整理成可分享的里程碑
&lt;/h1&gt;
&lt;h2&gt;
  
  
  背景
&lt;/h2&gt;

&lt;p&gt;Turing Sim 是一个实验性的 OpenUSD 编辑器和机器人仿真工作台。界面需要让三维视口保持中心地位，同时清晰地组织场景编辑、属性检查和仿真控制。项目还有一条重要约束：交互式 USD 修改必须进入文档命令历史，才能可靠地撤销和重做。&lt;/p&gt;

&lt;p&gt;项目负责人反馈，经过与 Claude Code 协作，界面已有明显改善。9 月 27 日的直接目标是把当前工作保存为可审查的 Git 提交，并同步到 GitHub。本次提交不仅包含视觉更新；提交日期并不能证明每项功能都是当天实现的。&lt;/p&gt;
&lt;h2&gt;
  
  
  目标与难点
&lt;/h2&gt;

&lt;p&gt;这次需要完整记录应用代码，避免把生成的运行日志混入版本库，完成可在无界面环境运行的验证，并与已发布的仓库同步。另一个难点是准确说明验证边界：本地 Python 环境缺少桌面编辑器所需的部分绑定。&lt;/p&gt;
&lt;h2&gt;
  
  
  解决思路与行动
&lt;/h2&gt;

&lt;p&gt;提交中的界面工作建立了共享的 Qt 设计系统，包括外观模式、可复用控件、图标和玻璃质感表面。配图展示了新版玻璃效果在窗口工具栏、停靠标签、面板和视口工具上的应用；还覆盖了停靠标签、工作区布局、编辑器工具栏、视口控件和机器人面板。另一部分工作加入了 Genesis 与 MuJoCo 的统一运行接口、MuJoCo 转换路径，以及通过可撤销文档命令进行的物理属性编辑。这些不同方向的改动被整理进同一个里程碑提交。&lt;/p&gt;

&lt;p&gt;完整的无界面测试运行结果为 372 项通过、14 项跳过，同时有 12 项失败和 3 项错误。部分编辑器测试无法导入 &lt;code&gt;pxr.Usdviewq&lt;/code&gt;；原生 schema 子进程测试在本地 macOS/OpenUSD 环境中崩溃。因此目前不能声称整套测试通过，仍需在目标运行环境继续验证。生成的 MuJoCo 警告日志没有纳入提交。&lt;/p&gt;

&lt;p&gt;刷新 GitHub 登录后，发现远端仓库具有独立的提交历史，但变更前的文件树与本地一致。于是将新提交接到远端 &lt;code&gt;main&lt;/code&gt; 的历史之后再推送，保留了远端已有历史。最终提交为 &lt;a href="https://github.com/lydd8888/TuringSim/commit/5881d6fbef146b244dbbc457cd9d90bcafa3cd45" rel="noopener noreferrer"&gt;&lt;code&gt;5881d6f&lt;/code&gt;&lt;/a&gt;。&lt;/p&gt;
&lt;h3&gt;
  
  
  初版与现在：界面变化
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjevzrl1w8qlp3fsoo85c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjevzrl1w8qlp3fsoo85c.png" alt="目前能找到的最早 Turing Sim 界面截图，玻璃风格改版之前" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;早期界面：深灰色平面面板与下划线标签。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq92cq3urakz8mc0dsomu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq92cq3urakz8mc0dsomu.png" alt="新版 Turing Sim 玻璃风格界面，包含场景树、视口和检查面板" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;当前界面：视口周围采用玻璃质感与分组控件。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;目前能找到的最早界面截图以较平面的深灰工作区为主：Objects/Layers 使用下划线标签，Inspector 固定在右侧，视口中只有较小的浮动工具条。新版截图则展示了视口周围的蓝色玻璃质感、胶囊形停靠标签与工具栏分组、可搜索的场景树，以及更醒目的浮动视口控件。两版都把三维画布放在视觉中心。&lt;/p&gt;

&lt;p&gt;两张截图的选择状态和相机视角不同，因此这里只比较布局与视觉语言，不据此判断渲染质量或性能。&lt;/p&gt;
&lt;h2&gt;
  
  
  结果与下一步
&lt;/h2&gt;

&lt;p&gt;这批界面与仿真工作现已记录在 GitHub，供团队审查。设计系统文档为后续面板提供统一的视觉语言；引擎适配文档则定义了比较不同运行后端的方式。文档明确指出，基准测试数值仍需实际运行并记录，当前不能据此得出性能结论。&lt;/p&gt;

&lt;p&gt;下一步是在受支持的 macOS 启动环境中验证编辑器和 schema，调查已观察到的失败，并在比较引擎性能前取得真实测量结果。&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>opensource</category>
      <category>robotics</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
