<p dir="auto">Hi all,</p>
<p dir="auto">while writing examples for <a href="http://grow3.zyxxy.de" rel="nofollow">grow3.js</a>, I found out that adding objects gets increasingly slower with more objects already in the scene. It gets apparent at &gt;&gt;10k objects in a scene.</p>
<p dir="auto">I tracked down the problem to <a href="https://github.com/mrdoob/three.js/blob/master/src/scenes/Scene.js#L43">this line in Scene.js</a>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="if ( this.__objects.indexOf( object ) === - 1 ) {"><pre class="notranslate"><code class="notranslate">if ( this.__objects.indexOf( object ) === - 1 ) {
</code></pre></div>
<p dir="auto">Usually objects you try to add to a scene aren't already in it, so the <code class="notranslate">indexOf()</code> statement searches through the whole array every time an object is added to the scene. This means a <strong>quadratic</strong> increase in computation time depending on the overall number of objects n.</p>
<p dir="auto"><code class="notranslate">Scene.__objects</code> seems to be used to prevent duplicate objects ending in <code class="notranslate">__objectAdded</code>. I'm not sure if this somewhat defensive code is needed - the objects aren't prevented to be added to the scene hierarchy anyway.</p>
<p dir="auto">So my proposal would be to just remove the __objects array. I did this <a href="https://github.com/filgf/three.js/blob/dev/src/scenes/Scene.js">here</a>. three.js examples seem to still work fine, performance while building scenes with many objects increases dramatically.</p>
<p dir="auto">An alternative might be to have some flag to explicitly disable dupes-checking at runtime.</p>
<p dir="auto">Finally some vague measurements (with my oldish Macbook) based on the <a href="https://github.com/mrdoob/three.js/blob/master/examples/webgl_performance.html">webgl_performance.html</a> example, by changing the number of objects (numbers = ms for initializing the scene):<br>
500 nodes: 200ms (no __objects: 95ms)<br>
20000 nodes: 1707ms (no __objects: 577ms)<br>
40000 nodes: 7000ms (no __objects: 744ms)</p>
<p dir="auto">If maintainers agree, I'd issue a pull request. Just didn't want to rush in, as this seems to apply to the very core of three.js...</p>
<p dir="auto">Cheers!</p>
<h5 dir="auto">Summary of feature request</h5>
<p dir="auto"><code class="notranslate">.addScaledVector</code> but instead of <em>adding</em> a <em>scaled</em> <em>vector</em>, <em>merging</em> a <em>transformed</em> <em>BufferGeometry</em></p>
<h5 dir="auto">Description of feature request</h5>
<p dir="auto">For optimization purposes, I want to render a single buffer geometry instead of rendering each object in my scene separately</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="const geometry = THREE.BufferGeometryUtils.mergeBufferGeometries(this.children.map(child =&gt;
    child.geometry.clone().applyMatrix4(child.matrix) // The issue
));
this.add(new THREE.Mesh(geometry, this.children[0].material));
for (const child of this.children) {
     child.visible = false;
}"><pre class="notranslate"><span class="pl-k">const</span> <span class="pl-s1">geometry</span> <span class="pl-c1">=</span> <span class="pl-c1">THREE</span><span class="pl-kos">.</span><span class="pl-c1">BufferGeometryUtils</span><span class="pl-kos">.</span><span class="pl-en">mergeBufferGeometries</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">children</span><span class="pl-kos">.</span><span class="pl-en">map</span><span class="pl-kos">(</span><span class="pl-s1">child</span> <span class="pl-c1">=&gt;</span>
    <span class="pl-s1">child</span><span class="pl-kos">.</span><span class="pl-c1">geometry</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><span class="pl-en">applyMatrix4</span><span class="pl-kos">(</span><span class="pl-s1">child</span><span class="pl-kos">.</span><span class="pl-c1">matrix</span><span class="pl-kos">)</span> <span class="pl-c">// The issue</span>
<span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-en">add</span><span class="pl-kos">(</span><span class="pl-k">new</span> <span class="pl-c1">THREE</span><span class="pl-kos">.</span><span class="pl-c1">Mesh</span><span class="pl-kos">(</span><span class="pl-s1">geometry</span><span class="pl-kos">,</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">children</span><span class="pl-kos">[</span><span class="pl-c1">0</span><span class="pl-kos">]</span><span class="pl-kos">.</span><span class="pl-c1">material</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-k">for</span> <span class="pl-kos">(</span><span class="pl-k">const</span> <span class="pl-s1">child</span> <span class="pl-k">of</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">children</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
     <span class="pl-s1">child</span><span class="pl-kos">.</span><span class="pl-c1">visible</span> <span class="pl-c1">=</span> <span class="pl-c1">false</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">(simplified code, not tested but should convey my point)</p>
<p dir="auto">Unfortunately, the existing buffer geometry merge methods don't support transforming each merged vertex by a custom matrix. That means I have to clone each child's geometry and apply the matrix to the entire clone at once.</p>
<p dir="auto">This is a waste of memory, and could be avoided with</p>
<ul dir="auto">
<li><code class="notranslate">BufferGeometry.merge(bufferGeometry, optionalMatrix)</code></li>
<li><code class="notranslate">BufferGeometryUtils.mergeBufferGeometries(bufferGeometries, optionalMatrices)</code></li>
</ul>
<p dir="auto">The 2nd option is slightly more awkward but is preferred because BufferGeometryUtils is lossless (doesn't require manual index argument)</p>
<p dir="auto">If a matrix is passed in, each vertex being merged in should have it applied.</p>
<p dir="auto">Note: I know I could get around this by pre-applyMatrix4-ing all my objects' geometries, but I don't want to do that...I want them to share a single, applyMatrix4'd buffer geometry instance.</p>
<p dir="auto">Note 2: For my use case, each child's BufferGeometry is the same. A solution specific to my case is :</p>
<ul dir="auto">
<li><code class="notranslate">BufferGeometryUtils.stamp(bufferGeometry, matrices)</code></li>
</ul>
<p dir="auto">Which would take a single geometry and stamp it at multiple locations/rotations/scales defined by each matrix.</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=""> r114</li>
</ul>