To change the size of a geometry somewhere in a scene graph, without affecting the size of children in the subtree, I am doing something like this:
mesh.geometry.dispose() // rid the old geometry
mesh.geometry = new SomeGeometry( /* new size values here */ )
Resizing a geometry like this is expensive because it recreates vertices all over again, and the old geometry needs to be garbage collected.
It is possible to achieve the same effect using scale
on the Mesh, which performs better, but has the unwated side-effect of also scaling children in the subtree.
Scale on a Geometry can currently only be set once and not in a loop, so we can’t get a performance boost using this feature.
If we could, the code would look like this:
const scale = { /* scale values to mimic size change */ }
mesh.geometry.scale.x = scale.x
mesh.geometry.scale.y = scale.y
mesh.geometry.scale.z = scale.z
If we scale a mesh instead:
const scale = { /* scale values to mimic size change */ }
mesh.scale.x = scale.x
mesh.scale.y = scale.y
mesh.scale.z = scale.z
then this affects all children, which is what I want to avoid.
It’d be great for this to work, so we can simply scale a Geometry inside a loop, but not scale an entire sub tree.
Sample Use Case
I’ve made some custom elements that render using CSS3D and WebGL (Three.js). They might remind you of A-Frame in some ways. I can do this with them:
<i-scene experimental-webgl="true">
<i-mesh id="first"
has="box-geometry basic-material"
size="2 2 2"
rotation="30 30 30"
color="0.5 0.5 0.5 1">
<h1> Hello "mixed mode" </h1>
<p>
This is DOM content, mixed with WebGL
</p>
<i-mesh id="second"
has="sphere-geometry basic-material"
size="1 1 1"
rotation="30 30 30"
color="0.5 0.5 0.5 1"
position="1.5 1.5 0" >
</i-mesh>
</i-mesh>
</i-scene>
This will render a scene that mixes DOM with WebGL in the same 3D space, similar to my example fiddle here.
Changing the size
attributes currently works. Internally it creates a new geometry with a new size, but as described above it is not performant if we want to animate geometry size without affecting children.
A unique feature of these custom elements is how sizing works. For example, we can write stuff like this:
<i-mesh id="second"
has="sphere-geometry basic-material"
sizeMode="literal proportional literal"
size="2 0.75 3">
</i-mesh>
where sizeMode
specifies the size mode per axis, which causes size values for each axis to have different meaning. A “literal” size mode for a given axis means the value for the axis dictates the literal size of the object along that axis (without affecting children in the sub tree), and a “proportional” size means that the object’s size is proportional to its parent.
In this way, children in a sub tree are only affected if they have proportional sizing, otherwise not if they have literal sizing.
So in the previous example, the mesh has a literal X size of 2, a Y size of 75% (0.75) of its parent, and a literal Z size of 3.
Some of you might remember this sort of sizing from the now-abandoned open source projects Famous/famous and Famous/engine. I liked this sizing concept, so I adopted it, and will also be adding other size modes for doing some neat tricks.
TLDR, I would like for resizing of geometries to be performant and to not affect children in the subtree, and to be able to do this in a render loop. Later on I’d like to let the user optionally choose the type of internal size method applied (scaling vs making new vertices). In some cases, scale can work and boosts performance. In other cases, scale might have an unwanted effect on textures, so resizing the geometry (making new vertices) might have better results but less performance. It may also be possible to scale both geometry and texture…
How can it be implemented?
A couple ideas:
- Geometries can have a matrix that is multiplied by their owner Mesh’s matrix when calculating world transforms during a render (f.e. in a render loop), rather than a matrix that is used only before first-render. Basically a Geometry would be treated like an Object3D on it’s own, branched away from any of the Mesh’s children.
- A downside is that shared geometries would all have the same scaling when the geometry belongs to multiple meshes anywhere in the scene, which may be unwanted and users would have to waste memory making new geometries so that meshes have their own geometry instances.
- This would replace the current Geometry instance API with one that can be modified in the render loop to create animation.
- Meshes would contain an extra geometry-specific matrix. The matrix would be applied to Geometries as if they were branched Object3Ds.
- This solves the downside of the previous option. Geometry transforms would be done using API on the Mesh instance, not on the Geometry instance.
- The existing Geometry instance API would still exist, and would only be useful before the first render and not in a loop, as currently. Instead we would use the Mesh API to transform whichever geometry it contains.
- This allows geometry sharing geometry transforms contained to their Meshes, which means better possible performance because one Gfeometry instance can be individually transformed on a per-Mesh basis.