<p dir="auto">I believe this bug has various scenarios for happening, but I've managed to pinpoint just one:</p>
<p dir="auto">When opening the editor, (only) after adding some object, and duplicating the tab, some settings get corrupt. At the least the keyboard shortcuts are corrupted, but I have reason to believe it may be more than that.</p>
<details open="" class="details-reset border rounded-2">
  <summary class="px-3 py-2">
    <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-device-camera-video">
    <path d="M16 3.75v8.5a.75.75 0 0 1-1.136.643L11 10.575v.675A1.75 1.75 0 0 1 9.25 13h-7.5A1.75 1.75 0 0 1 0 11.25v-6.5C0 3.784.784 3 1.75 3h7.5c.966 0 1.75.784 1.75 1.75v.675l3.864-2.318A.75.75 0 0 1 16 3.75Zm-6.5 1a.25.25 0 0 0-.25-.25h-7.5a.25.25 0 0 0-.25.25v6.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-6.5ZM11 8.825l3.5 2.1v-5.85l-3.5 2.1Z"></path>
</svg>
    <span aria-label="Video description kEMPWgZRAL.mp4" class="m-1">kEMPWgZRAL.mp4</span>
    <span class="dropdown-caret"></span>
  </summary>

  <video src="https://user-images.githubusercontent.com/31798283/125646841-3c06b6fc-60a9-4f31-ac73-3bf6a31bc948.mp4" data-canonical-src="https://user-images.githubusercontent.com/31798283/125646841-3c06b6fc-60a9-4f31-ac73-3bf6a31bc948.mp4" controls="controls" muted="muted" class="d-block rounded-bottom-2 border-top width-fit" style="max-height:640px; min-height: 200px">

  </video>
</details>

<ul dir="auto">
<li>Device: Desktop</li>
</ul>
<p dir="auto">Hi,</p>
<p dir="auto">In my current project I have to implement some complex 2D objects (multilayered GUI) and those will be anchored in 3D space.</p>
<p dir="auto">I have noticed lots of artifacts on those planar 2D objects caused by z-index sorting of 2D layers in the same GUI object. There are few ways to solve it:</p>
<ul dir="auto">
<li>disable sorting and use global <code class="notranslate">THREE.Object3D.renderOrder</code> to sort items - this is not the best option as I would need to keep track of <code class="notranslate">THREE.Object3D.renderOrder</code> globally and update it on many objects if only one item gets inserted in the middle.</li>
<li>render to off-screen texture first - could work but my GUI is quite dynamic and I gain no perf when doing so and it requires me to update render targets on window / GUI resize.</li>
<li>apply some small Z epsilon value to separate layers so depth buffer works fine - tried that but this doesn't work 100%. I don't want to limit myself on how far/close to the object the camera can be.</li>
<li>Create multiple scenes and do few separate render passes - won't work for my project as it would make my project non-extensible or at least it would make it very hard.</li>
</ul>
<p dir="auto">None of the above are good enough nor easy to implement. IMO the cleanest solution to the problem (which I implemented and tested) was to modify the way WebGLRendererList sorts items by <strong>adding possibility to group render items</strong> into some kind of layers and then play with rather "local" than "global" <code class="notranslate">THREE.Object3D.renderOrder</code> property.</p>
<p dir="auto">The easiest implementation of this feature would look like this:</p>
<blockquote>
<p dir="auto">Object3D.js</p>
</blockquote>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="class Object3D {
  // Group render items by this value
  renderOrderGroup: number;
}"><pre class="notranslate"><code class="notranslate">class Object3D {
  // Group render items by this value
  renderOrderGroup: number;
}
</code></pre></div>
<blockquote>
<p dir="auto">RenderItem.js</p>
</blockquote>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="class Object3D {
  // Group render items by this value
  renderOrderGroup: number;
}"><pre class="notranslate"><code class="notranslate">class Object3D {
  // Group render items by this value
  renderOrderGroup: number;
}
</code></pre></div>
<blockquote>
<p dir="auto">WebGLRendererLists.js</p>
</blockquote>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="// Render item would look something like this
renderItem = {
   id: object.id,
   object: object,
   geometry: geometry,
   material: material,
   program: material.program,
   renderOrder: object.renderOrder,
   renderOrderGroup: object.renderOrderGroup,
   z: z,
   group: group
};

// Sorting extended by this
function painterSortStable(a: RenderItem, b: RenderItem) {
    if (a.renderOrderGroup !== b.renderOrderGroup) {
        return (a.renderOrderGroup || Number.MIN_SAFE_INTEGER) - (b.renderOrderGroup || Number.MIN_SAFE_INTEGER);
    } 
    // ... followed by rest of the sorting comparisons
}"><pre class="notranslate"><code class="notranslate">// Render item would look something like this
renderItem = {
   id: object.id,
   object: object,
   geometry: geometry,
   material: material,
   program: material.program,
   renderOrder: object.renderOrder,
   renderOrderGroup: object.renderOrderGroup,
   z: z,
   group: group
};

// Sorting extended by this
function painterSortStable(a: RenderItem, b: RenderItem) {
    if (a.renderOrderGroup !== b.renderOrderGroup) {
        return (a.renderOrderGroup || Number.MIN_SAFE_INTEGER) - (b.renderOrderGroup || Number.MIN_SAFE_INTEGER);
    } 
    // ... followed by rest of the sorting comparisons
}
</code></pre></div>
<p dir="auto">Being able to group items with layers is a nice and clean way to solve the limitation of ThreeJS (big one in my project at least) when working with GUI/2D objects embedded in 3D space.</p>
<p dir="auto">Another way (even more extensible IMO) would be to add support for customizing sorting logics so app can do whatever crazy stuff they want (i.e. via data stored in <code class="notranslate">THREE.Object3D.userData</code> property).<br>
One would simply pass custom sorter into the <code class="notranslate">THREE.WebGLRendererLists</code> or even pass entirely new implementation to the <code class="notranslate">THREE.WebGLRenderer.renderLists</code> property.</p>
<p dir="auto">I could make a PR with either of the proposed changes if anyone thinks it is a good idea. Otherwise I will stick with my little hack where I override the prototype of the WebGLRendererLists - ugly but works for me now. :)</p>
<p dir="auto">Thanks,<br>
Lukasz</p>