<p dir="auto">I was looking at the ThreeJS materials.  There are four materials that are nearly the same thing.  Basic, Lambert, Phong and NormalMap.</p>
<p dir="auto">NormalMap seems to have all the features of Lambert and Phong and even has the ability to turn off things like Specular (to create a Lambert material.)</p>
<p dir="auto">I think that Basic is a material where color is set into emissive and it isn't affected by lights.</p>
<p dir="auto">Thus maybe four different materials could be combined into one.</p>
<p dir="auto">I am wondering if it is worth keeping all of these materials around on the shader side of things.</p>
<p dir="auto">I think that with proper use of DEFINES (as it is already done) we can have for the most part a single vertex / fragment shader pair for all standard materials.  The variations can be completely controlled via the DEFINES rather than constructing new shaders via including snippets.  I think that with proper use of DEFINES in the more complex shader you can still get the speed you would get from a simple Lambert shaders.</p>
<p dir="auto">One full shader is a lot easier to maintain than three (or four) very similar shaders.  (I also struggle trying to follow the code because of the way the snippets are combined -- I wonder if there is an alternative design possible here that is easier to grok.)</p>
<p dir="auto">Even if we combined things, we could still keep the existing material classes around for creating specialized materials for those that do not want to deal with all the options explicitly.  Or one can create a factory class for creating common sub-types of StandardMaterials.  But underneath these would all refer to the same shader, they would just provide simplified parameters to users.</p>
<p dir="auto">BTW I am open to other design suggestions regarding the shaders.  I do not have that much experience with dynamic shader generation designs.</p>
<p dir="auto">(I push this in part because the more we use a single shader for everything the more can group together objects into batches...  Now there are some additional complications, specifically one needs to be able to control the variations without defines to do full batches, but that can come later as an option I think.)</p>
<p dir="auto"><strong>Note:</strong> I don't want to bury the lede for this request, so I moved a brief summary to the top of the issue. Please refer to the second and third headings for a more detailed background and rationale!</p>
<h5 dir="auto">Proposal</h5>
<p dir="auto">When loading models with <code class="notranslate">GLTFLoader</code>, it would be nice if we could retain associations between glTF scene graph elements and the Three.js objects that correspond to them. I briefly discussed this problem with <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/donmccurdy/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/donmccurdy">@donmccurdy</a> and he suggested that one of the following approaches might be acceptable:</p>
<ol dir="auto">
<li>Decorate Three.js objects via <code class="notranslate">userData</code> e.g., <code class="notranslate">material.userData.gltfMeta.index = 3</code></li>
<li>Expose a lookup table somewhere e.g., <code class="notranslate">parser.getDefinition( material: THREE.Material ): GLTF.Material</code></li>
</ol>
<p dir="auto">What do others think about this problem? Would you be willing to consider a change that makes either of the suggested enhancements?</p>
<h5 dir="auto">Background</h5>
<p dir="auto">Three.js' <code class="notranslate">GLTFLoader</code> currently produces an artifact that contains a Three.js scene graph as well as a <code class="notranslate">GLTFParser</code> instance that holds the original, deserialized glTF.</p>
<p dir="auto">The <code class="notranslate">GLTFParser</code> holds associations between the elements in the original glTF and the Three.js scene graph. These associations are expressed both by <code class="notranslate">GLTFParser.prototype.getDependencies</code> and the <code class="notranslate">cache</code> object on the <code class="notranslate">GLTFParser</code> instance.</p>
<p dir="auto">In some cases, the relationship between the original glTF element and the associated Three.js objects is obscured and cannot be trivially re-established. For example, materials associated with a skinned mesh are cloned, and the resulting clones are cached against a key that cannot be reconstructed easily:</p>
<p dir="auto"></p><div class="Box Box--condensed my-2">
  <div class="Box-header f6">
    <p class="mb-0 text-bold">
      <a href="https://github.com/mrdoob/three.js/blob/648c4bbd7d8dd354680d720578da64a77548eecf/examples/jsm/loaders/GLTFLoader.js#L2118-L2127">three.js/examples/jsm/loaders/GLTFLoader.js</a>
    </p>
    <p class="mb-0 color-fg-muted">
        Lines 2118 to 2127
      in
      <a data-pjax="true" class="commit-tease-sha" href="/mrdoob/three.js/commit/648c4bbd7d8dd354680d720578da64a77548eecf">648c4bb</a>
    </p>
  </div>
  <div itemprop="text" class="Box-body p-0 blob-wrapper blob-wrapper-embedded data">
    <table class="highlight tab-size mb-0 js-file-line-container" data-tab-size="8" data-paste-markdown-skip="">

        <tbody><tr class="border-0">
          <td id="L2118" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="2118"></td>
          <td id="LC2118" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-s1">cachedMaterial</span> <span class="pl-c1">=</span> <span class="pl-s1">material</span><span class="pl-kos">.</span><span class="pl-en">clone</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span> </td>
        </tr>

        <tr class="border-0">
          <td id="L2119" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="2119"></td>
          <td id="LC2119" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line">  </td>
        </tr>

        <tr class="border-0">
          <td id="L2120" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="2120"></td>
          <td id="LC2120" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-k">if</span> <span class="pl-kos">(</span> <span class="pl-s1">useSkinning</span> <span class="pl-kos">)</span> <span class="pl-s1">cachedMaterial</span><span class="pl-kos">.</span><span class="pl-c1">skinning</span> <span class="pl-c1">=</span> <span class="pl-c1">true</span><span class="pl-kos">;</span> </td>
        </tr>

        <tr class="border-0">
          <td id="L2121" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="2121"></td>
          <td id="LC2121" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-k">if</span> <span class="pl-kos">(</span> <span class="pl-s1">useVertexTangents</span> <span class="pl-kos">)</span> <span class="pl-s1">cachedMaterial</span><span class="pl-kos">.</span><span class="pl-c1">vertexTangents</span> <span class="pl-c1">=</span> <span class="pl-c1">true</span><span class="pl-kos">;</span> </td>
        </tr>

        <tr class="border-0">
          <td id="L2122" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="2122"></td>
          <td id="LC2122" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-k">if</span> <span class="pl-kos">(</span> <span class="pl-s1">useVertexColors</span> <span class="pl-kos">)</span> <span class="pl-s1">cachedMaterial</span><span class="pl-kos">.</span><span class="pl-c1">vertexColors</span> <span class="pl-c1">=</span> <span class="pl-c1">true</span><span class="pl-kos">;</span> </td>
        </tr>

        <tr class="border-0">
          <td id="L2123" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="2123"></td>
          <td id="LC2123" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-k">if</span> <span class="pl-kos">(</span> <span class="pl-s1">useFlatShading</span> <span class="pl-kos">)</span> <span class="pl-s1">cachedMaterial</span><span class="pl-kos">.</span><span class="pl-c1">flatShading</span> <span class="pl-c1">=</span> <span class="pl-c1">true</span><span class="pl-kos">;</span> </td>
        </tr>

        <tr class="border-0">
          <td id="L2124" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="2124"></td>
          <td id="LC2124" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-k">if</span> <span class="pl-kos">(</span> <span class="pl-s1">useMorphTargets</span> <span class="pl-kos">)</span> <span class="pl-s1">cachedMaterial</span><span class="pl-kos">.</span><span class="pl-c1">morphTargets</span> <span class="pl-c1">=</span> <span class="pl-c1">true</span><span class="pl-kos">;</span> </td>
        </tr>

        <tr class="border-0">
          <td id="L2125" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="2125"></td>
          <td id="LC2125" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-k">if</span> <span class="pl-kos">(</span> <span class="pl-s1">useMorphNormals</span> <span class="pl-kos">)</span> <span class="pl-s1">cachedMaterial</span><span class="pl-kos">.</span><span class="pl-c1">morphNormals</span> <span class="pl-c1">=</span> <span class="pl-c1">true</span><span class="pl-kos">;</span> </td>
        </tr>

        <tr class="border-0">
          <td id="L2126" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="2126"></td>
          <td id="LC2126" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line">  </td>
        </tr>

        <tr class="border-0">
          <td id="L2127" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="2127"></td>
          <td id="LC2127" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">cache</span><span class="pl-kos">.</span><span class="pl-en">add</span><span class="pl-kos">(</span> <span class="pl-s1">cacheKey</span><span class="pl-kos">,</span> <span class="pl-s1">cachedMaterial</span> <span class="pl-kos">)</span><span class="pl-kos">;</span> </td>
        </tr>
    </tbody></table>
  </div>
</div>
<p></p>
<h5 dir="auto">Description of the problem</h5>
<p dir="auto">Consider a model editor that allows a user to:</p>
<ol dir="auto">
<li>Load a glTF with any shape</li>
<li>Interactively change the value of any field of any element of the glTF</li>
<li>See the result of the change rendered in real time</li>
<li>Export a new glTF</li>
</ol>
<p dir="auto">In an ideal world, the editor should faithfully re-export the model with its original hierarchy and shape. The only meaningful change in the exported file should be the one made interactively by the user.</p>
<p dir="auto">In the pursuit of this goal, it is very useful (and probably required) that an association between the source glTF scene graph and the Three.js scene graph be made, so that for any change we make to a glTF element, we can reflect that change in the rendered Three.js scene in real-time without completely re-loading the glTF.</p>
<p dir="auto">Some of the information required to correctly correlate the glTF scene graph with the Three.js scene graph is lost or obscured, and this makes the above described editor scenario very difficult (perhaps impossible) to achieve.</p>
<h5 dir="auto">Three.js version</h5>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> Dev</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> r115</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> ...</li>
</ul>
<h5 dir="auto">Browser</h5>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> All of them</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> Chrome</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> Firefox</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> Internet Explorer</li>
</ul>
<h5 dir="auto">OS</h5>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> All of them</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> Windows</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> macOS</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> Linux</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> Android</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> iOS</li>
</ul>
<h5 dir="auto">Hardware Requirements (graphics card, VR Device, ...)</h5>