Closed mrdoob closed 10 years ago
Couldn't you call it Shape
? I'm lazy as hell when I type and Geometry2
feels like getElementsByClass
. And that would prevent the backwards compatibility break.
Hey! I think this is great. One idea would be to allow indices as long as the referenced arrays (vertices, uvs etc.) are within Uint16 - simply check it and throw an error if any of the arrays surpasses the limit.
Even though I haven't seen any major performance impact running drawArrays on desktops, you do lift some work of the vertex shader using indices. And that's probably a very good thing on mobiles.
@rafaelcastrocouto Geometry2
is a temporal name. It will eventually replace Geometry
.
@empaempa True that! Maybe a IndexedGeometry
could be created and have such limit check.
Yes, that's a good idea to split them up. I guess a usability question would be what devs think of when thinking of "geometry" - does it use indices or not? If so, maybe UnindexGeometry ;)
This is great! One thing though: I work with IFC files, which come with their own UUID's, so possibility of setting the UUID via constructor would come handy. Thanks
One thing though: I work with IFC files, which come with their own UUID's, so possibility of setting the UUID via constructor would come handy.
Can't you change the property directly?
geometry.uuid = new_uuid;
@mrdoob : Sure I can. As I said, it would come handy. But, since I'm not using generated UUID, and my files have thousands of objects, so there is a lot of unnecessary calculations done . I can also use the custom geometry.js for my needs. It was just my two cents...
One thing we may want to do is always use 32bit ints for the indices. I did some checks and nearly every device and PC I could find supports the 32bit index webgl extension. Having to split meshes into 16bit chuncks is brutal and generally unnecessary.
BTW I think this change rocks. :) Just be sure to allow for multiple UV sets, tangents, binormals, etc...
One thing we may want to do is always use 32bit ints for the indices. I did some checks and nearly every device and PC I could find supports the 32bit index webgl extension.
Interesting...
BTW I think this change rocks. :) Just be sure to allow for multiple UV sets, tangents, binormals, etc...
Will do! ^^
This will be the most important and awesome change to three.js in a long time, in my opinion. I've been actually thinking about replacing three.js by some other library because the overhead of all those Vector3
s was getting too big.
How will the new Geometry
handle MeshFaceMaterial
s? Those cause overhead as well since the engine has to split faces by the face materials. Are you dropping MeshFaceMaterial
and only allowing one material per Geometry
? Or will the geometry consist of multiple parts, each having one material and one range of vertices/indices that belong to it?
Dropping indices will increase the file size of models (not sure by how much on average), or require loaders to de-index the data. I was hoping three.js would eventually adapt a glTF-like file format that does not require any preprocessing at all during loading (glTF loads binary blobs into typed arrays and passes the data directly to webgl buffers).
Are you dropping
MeshFaceMaterial
and only allowing one material perGeometry
?
I think so. Yes.
I was hoping three.js would eventually adapt a glTF-like file format that does not require any preprocessing at all during loading (glTF loads binary blobs into typed arrays and passes the data directly to webgl buffers).
I think the new structure should be perfect for loading gITF. Shouldn't it?
I think so. Yes.
This is only indirectly related to the new Geometry
design, but if you're removing MeshFaceMaterial (which I think is a good idea), you might have to adapt other interfaces to simplify the usage of multi-material objects. I assume multi-material Mesh
objects won't be supported at all and will have to be implemented via multiple Mesh
objects.
Mesh
/ SkinnedMesh
/ MorphAnimMesh
hierarchy).I think the new structure should be perfect for loading gITF. Shouldn't it?
It should. But if you don't support indices at all, vertices have to be duplicated before saving the file, which might increase the file size. It's just a potential disadvantage of not supporting indexed geometry.
i think helper methods in TypedGeometry
is a great idea. Would the usual Vector3
s be depreciated or merged into it?
Right now I'm assume everytime you do a geometry.vertex(1)
it would return you a new TypedVector3
? I think the overheads shouldn't be much, but if it was run in a loop, would it better to to allow copying? eg.
typeVector = new THREE.TypeVector3();
for (i=0; i<10000;i++) {
geometry.vertex(i, typeVector);
typeVector.set(x, y, z);
}
Right now it seems that the typed arrayed in Geometry2
are pre-allocated too. I'm thinking about applications which would require changing the amount of vertices, eg a modelling app for example. Would it be better to pre-allocate a large array first, re-create the arrays, or expand them? (SubdivisionModifier
might also find "producing" a new geometry easier than "modifying" a existing one).
Also, any thoughts on how Geometry2
may support a half-edge mesh representation?
Lastly, any planned timeline for supporting Geometry2
across the renderers esp. WebGLRenderer
? That may affect how soon we should start thinking geometries in the new format :D
Would the usual Vector3s be depreciated or merged into it?
I really like the math library of ThreeJS and I would suggest keeping it. I would suggest not using it in Geometry2 though. Geometry2 should use flat arrays that are easily uploaded to WebGL. But keeping around Vector3 is useful for doing the math that drives the engine -- all game engines have a 3D math library similar to ThreeJS even if they have flat arrays for their renderable items.
I'm thinking about applications which would require changing the amount of vertices, eg a modelling app for example.
A flat array is by far the best representation, even for modelers.
(SubdivisionModifier might also find "producing" a new geometry easier than "modifying" a existing one).
While this is a useful function for modelling (we use it in http://clara.io), it isn't intended for real-time engines as that type modifier is very slow. No game engine that I know of uses SubD in real-time, except on the GPU as real-time tesselation (which is slightly different) I do not think one should optimize a WebGL-oriented rendering engine to support SubdivisionModifier. Rather one should focus on real-time tesselation methods via a geometry shader (but alas this is not yet supported in WebGL.)
Also, any thoughts on how Geometry2 may support a half-edge mesh representation?
No game engines support such a structure. Half-Edge mesh representations are for editing. Usually one translates these structures to flat arrays for rendering. Even game engines with dynamic geometry (such as fracturing engines) do not use half-edge mesh representations.
@bhouston i'm not against the idea of flat arrays but rather curious to know how you or others would support dynamic geometry with flat arrays (Geometry2
) - over allocate, reallocate, or expand the flat arrays?
As for the half-edge representation referenced in The ryg blog, it seems like he converts that data structure into flat arrays too, but considering that he probably uses that for real-time demos, its a possibility someone may write half-edge meshes that translates to Geometry2
. If so, I think it would be a bonus for more efficient subdivisions, or modelling applications.
i'm not against the idea of flat arrays but rather curious to know how you or others would support dynamic geometry with flat arrays
- If you don't modify the topology of the geometry: Use a vertex shader. If this is not possible, modify vertex data in-place, either using the API mrdoob described (
geometry.face( 10 ).vertex( 1 ).x=10;
), or accessing the raw data.- If you modify the topology (e.g., insert new vertices): create a new geometry object from scratch and delete the old one. If you had some kind of dynamic mesh structure, this is what you would have to do anyway before sending the new vertex data to the GPU.
Also, any thoughts on how Geometry2 may support a half-edge mesh representation?
You could build a separate half-edge mesh structure that outputs corresponding flat arrays when desired. A common use case for a javascript render engine is to load some geometry and display it in real time, without any modifications (game engines, 3d product previews). If you want this to be performant on mobile devices, having flat arrays that are sent 1:1 to the GPU is pretty much the only option. Anything with lots of small objects will have a negative impact on the memory performance of the engine.
If so, I think it would be a bonus for more efficient subdivisions, or modelling applications.
The structures one wants for modeling application (and I know as we created http://clara.io), is not really like Geometry2. It is quite a bit different. And it actually can not be displayed directly, rather we translate our modeling structure into THREE.Geometry (and hopefully soon THREE.Geometry2) for display.
Real-time display of geometry is sort of a different problem than editing/modeling of geometry. I think it is best to have different structures for them so that each structure is efficient at its task, rather than finding an intermediate structure that is suboptimal for both tasks. (Or excessively complex.)
i see, Geometry2
now sounds more like a wrapper of elements/buffers for the GPU.
The structures one wants for modeling application (and I know as we created http://clara.io), is not really like Geometry2. It is quite a bit different. And it actually can not be displayed directly, rather we translate our modeling structure into THREE.Geometry (and hopefully soon THREE.Geometry2) for display.
Why aren't you translating to THREE.BufferGeometry?
Lastly, any planned timeline for supporting
Geometry2
across the renderers esp.WebGLRenderer
? That may affect how soon we should start thinking geometries in the new format :D
It should already work... f1630e92515b745fc223e710527c2a29c75fce58 :)
I'm thinking, however, that maybe Geometry2
should just extend BufferGeometry
...
I'm thinking, however, that maybe
Geometry2
should just extendBufferGeometry
...
Hmmm, it may not be a bad idea... 8adc04897803ca29b4b996fd206cd9779e6d0d7e
Why aren't you translating to THREE.BufferGeometry?
Our dynamic topology means we can change vertex counts and we can easily exceed the int16 limit to the indices. We do not want at this point to manage multiple THREE.BufferGeometry's, so we are letting ThreeJS's automatic splitting of Geometry do it this way. But it is really slow when this happens. Thus we would prefer for ThreeJS to add support int32 index buffers (and possibly drop int16 index buffer support, but I am not ideological on this, I just think it isn't useful) so splitting is not necessary and we can use a single THREE.BufferGeometry per object in our scene.
The other thing that would help is the ability to share buffers between THREE.BufferGeometries. One thing we do is have wireframes and solid meshes draw for the same object. The wireframe for a polymesh and its solid representation share the same vertices and UVs and normals, although the index buffers for the line set and the triangulated surface differ.
In part this sharing is important to us for maximum speed, but I understand we are relatively unique in this area so I am not pushing on it. That prototype direct renderer I wrote supports this type of structure -- sharing vertices and UV between a lineset and a triangulated surface.
In part this sharing is important to us for maximum speed, but I understand we are relatively unique in this area so I am not pushing on it. That prototype direct renderer I wrote supports this type of structure -- sharing vertices and UV between a lineset and a triangulated surface.
I think in the end that we will likely have to move towards something like that direct renderer approach (allowing one to skip the intermediate ThreeJS scene representation) I wrote because it can give us gains by being nearly perfectly optimal (both for speed and for GPU/CPU memory usage) for our case at the cost of being a bit difficult to use. It is just a question of getting the time to finish the direct renderer and unifying the shaders as much as possible.
Thus we would prefer for ThreeJS to add support int32 index buffers (and possibly drop int16 index buffer support, but I am not ideological on this, I just think it isn't useful) so splitting is not necessary and we can use a single THREE.BufferGeometry per object in our scene.
You mean using the OES_element_index_uint
extension?
Would interleaved attributes be of consideration? See http://i-am-glow.com/?p=165
While on this topic of geometry performance, it maybe also worth taking a look at the rendering approach done in @empaempa's GLOW library.
You mean using the OES_element_index_uint extension?
Yes. :)
Would interleaved attributes be of consideration?
While they can offer a bit of improved performance - or so they say, they are horrible to update in response to changes as you need to update the whole inteleaved buffer rather than just one attribute-specific flat array. Also the interleaved structure changes based on which attributes are enabled which can make things a bit complex.
@zz85 Interleaving is cool for (the bus') performance but really painful to implement. I'd leave it on the roadmap for now ;)
@bhouston: #4467 :D
@gero3 @mrdoob Whoa, sweet! Thank you!
@crobi & @mrdoob. About leaving one material per Geometry. 3d artists use multi-materials ("MM" hereafter) in practice rather often. Their exclusion from the engine can cause problems.
Artificial splitting of a MM object to separate parts by material can be a bad idea. For example, let we deform a MM surface. It can be hard to provide its continuity after splitting sometimes. Or, in some cases a user needs to know that "this set of objects" really is a single "logical" object, therefore he must maintain additional data and code to support this. Such things look unnatural.
Now you are providing these excelent things (continuity and others) in Three.js (thank you!). After MM exclusion, some users will have to reproduce these tricky things each time in own code.
p.s. I hope that I understood you incorrectly and you are not going to remove MM support.
@a-plotnik This is true, but in the realtime world MM isn't widely supported - at least not on weak platforms such mobiles and web. I'd rather see some way to easily combine shaders/materials into one shader to simulate MM :)
@a-plotnik It is true that DirectX (both the old style v9 and the new style v10/v11) supports multi-material meshes, but it does so in a simplier and more restricted way than ThreeJS's current support. The way that DirectX has done multiple materials is to allow one to render subsets of the index buffer -- such as you can render from 0-1100 and then later you can render 1101-2500. One usually applies different materials to each of these contiguous subset of the index buffer. This means that you do not have a per face material index as ThreeJS has it now. It also means that the renderer doesn't have to do much at all to support multimaterials in this fashion -- it just has to render a subset of the indices.
If ThreeJS wanted to support multi-materials in this fashion, the way that it would do it would be to have a list of subset ranges. And one would be able to apply materials to each subset as an alternative to setting on the mesh as a whole. This would be very fast, the only cost would be switching materials as all the buffers would remain loaded and unchanged.
I am not sure this is a priority for us, I'm just sharing this so one can learn from how DirectX does it.
Hmmm... BufferGeometry
kind of already supports this with its offsets
array... ^^
@empaempa My current target is browsers on desktops. Mobile devices with new surprises are expected later, so thank you for warnings.
@bhouston Thanks a lot, the situation becomes more clear.
I worried that multi-materials can disappear from high level api, may be with integrity of corresponding objects. I didn't try to touch internal Geometry representation.
@mrdoob Thank you for this whole topic. I used only Geometry before and looked at it only.
@mrdoob have a look at https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView This is probably what could make whole typed array thing work in context of geometry. A view can specify an offset, and since it's part of the language - you can probably expect higher performance. That and the fact that you get a pointer to section of a buffer, which is really want you seem to be trying to do.
On the other note - i'm not quite sure what is the fundamental issue with current Geometry and how far implications of replacing it would go, could you clarify? Generally there will always be trade-offs in implementation, it would be cool if design could be driven by ease of use primarily, as well as keeping this API consistent mostly.
@Usnul your comment made me try this:
THREE.Geometry3 = function ( size ) {
THREE.BufferGeometry.call( this );
var verticesBuffer = new ArrayBuffer( size * 3 * 4 );
var normalsBuffer = new ArrayBuffer( size * 3 * 4 );
var uvsBuffer = new ArrayBuffer( size * 2 * 4 );
this.vertices = [];
this.normals = [];
this.uvs = [];
for ( var i = 0; i < size; i ++ ) {
this.vertices.push( new Float32Array( verticesBuffer, i * 3 * 4, 3 ) );
this.normals.push( new Float32Array( normalsBuffer, i * 3 * 4, 3 ) );
this.uvs.push( new Float32Array( uvsBuffer, i * 2 * 4, 2 ) );
}
this.attributes[ 'position' ] = { array: new Float32Array( verticesBuffer, 0, size * 3 ), itemSize: 3 };
this.attributes[ 'normal' ] = { array: new Float32Array( normalsBuffer, 0, size * 3 ), itemSize: 3 };
this.attributes[ 'uv' ] = { array: new Float32Array( uvsBuffer, 0, size * 2 ), itemSize: 2 };
};
THREE.Geometry3.prototype = Object.create( THREE.BufferGeometry.prototype );
This results in a nice-ish API...
geometry.vertices[ 0 ][ 0 /*x*/ ] = 1;
Sadly, creating all these ArrayBufferView
s is slow.
I was discussing this briefly with jetienne and merpnderp on irc earlier and we had the idea of using accessors and proxy objects to help with this sort of thing. Here's my implementation of that. This allows you to do any of the normal operations you'd expect to be able to do on normal vertices, and is identical to how you would work with the old THREE.Geometry:
var geom = new THREE.Geometry4(10);
geom.vertices[0].set(1, 2, 3);
geom.vertices[1].copy(someOtherVec);
geom.vertices[2].addVectors(geom.vertices[0], geom.vertices[1]);
geom.vertices[3].y = 10;
console.log(geom.vertices[2]);
Here's what the code looks like:
THREE.Geometry4 = function ( size ) {
THREE.BufferGeometry.call( this );
var verticesBuffer = new ArrayBuffer( size * 3 * 4 );
var normalsBuffer = new ArrayBuffer( size * 3 * 4 );
var uvsBuffer = new ArrayBuffer( size * 2 * 4 );
this.attributes[ 'position' ] = { array: new Float32Array( verticesBuffer, 0, size * 3 ), itemSize: 3 };
this.attributes[ 'normal' ] = { array: new Float32Array( normalsBuffer, 0, size * 3 ), itemSize: 3 };
this.attributes[ 'uv' ] = { array: new Float32Array( uvsBuffer, 0, size * 2 ), itemSize: 2 };
this.vertices = new THREE.VectorArrayProxy( this.attributes[ 'position' ] );
this.normals = new THREE.VectorArrayProxy( this.attributes[ 'normal' ] );
this.uvs = new THREE.VectorArrayProxy( this.attributes[ 'uv' ] );
};
THREE.Geometry4.prototype = Object.create( THREE.BufferGeometry.prototype );
THREE.VectorArrayProxy = function(attribute) {
// Acts as a proxy for an array of vectors, by setting up accessors which return THREE.Vector*Proxy objects
this.attribute = attribute;
for (var i = 0, l = this.attribute.array.length / this.attribute.itemSize; i < l; i++) {
Object.defineProperty(this, i, {
get: (function(i) { return function() { return this.getValue(i); }})(i),
set: (function(i) { return function(v) { return this.setValue(i, v); }})(i),
});
}
}
THREE.VectorArrayProxy.prototype.getValue = function(i) {
// Allocates a new THREE.Vector2Proxy or THREE.Vector3Proxy depending on the itemSize of our attribute
var subarray = this.attribute.array.subarray(i * this.attribute.itemSize, (i + 1) * this.attribute.itemSize);
switch (this.attribute.itemSize) {
case 2:
return new THREE.Vector2Proxy(subarray);
case 3:
return new THREE.Vector3Proxy(subarray);
}
}
THREE.VectorArrayProxy.prototype.setValue = function(i, v) {
var vec = this[i];
vec.copy(v);
}
// Vector Proxy Objects
THREE.Vector2Proxy = function(subarray) {
this.subarray = subarray;
}
THREE.Vector2Proxy.prototype = Object.create( THREE.Vector2.prototype );
Object.defineProperty(THREE.Vector2Proxy.prototype, 'x', { get: function() { return this.subarray[0]; }, set: function(v) { this.subarray[0] = v; } });
Object.defineProperty(THREE.Vector2Proxy.prototype, 'y', { get: function() { return this.subarray[1]; }, set: function(v) { this.subarray[1] = v; } });
THREE.Vector3Proxy = function(subarray) {
this.subarray = subarray;
}
THREE.Vector3Proxy.prototype = Object.create( THREE.Vector3.prototype );
Object.defineProperty(THREE.Vector3Proxy.prototype, 'x', { get: function() { return this.subarray[0]; }, set: function(v) { this.subarray[0] = v; } });
Object.defineProperty(THREE.Vector3Proxy.prototype, 'y', { get: function() { return this.subarray[1]; }, set: function(v) { this.subarray[1] = v; } });
Object.defineProperty(THREE.Vector3Proxy.prototype, 'z', { get: function() { return this.subarray[2]; }, set: function(v) { this.subarray[2] = v; } });
I haven't benchmarked this, so I don't know how this would perform with highly-dynamic geometry. If the idea of using accessor functions so heavily isn't too much of a turn-off, it would be worth benchmarking to see what sort of performance impact this has in various use cases.
Nice! It's similar to the approach in #3672 but I hadn't came up with the idea of creating all the getters/setters for i
:)
Also the idea of extending Vector3
and patching its x
, y
and z
...
I also think that creating properties per element in the large array is highly problematic in terms of memory usage -- now you have two pointers per item of the original array and remember that pointers are at least 32 bits, thus you have probably doubled memory consumption, and it is probably worse than that. I'd recommend against being too fancy.
Sadly...
new THREE.Geometry3( 200 ); // 1.378ms
new THREE.Geometry4( 200 ); // 8.273ms
I also think that creating properties per element in the large array is highly problematic in terms of memory usage -- now you have two pointers per item of the original array and remember that pointers are at least 32 bits, thus you have probably doubled memory consumption, and it is probably worse than that. I'd recommend against being too fancy.
Yep! There has to be a way though... :)
@jbaicoianu here's a more efficient approach:
THREE.Geometry5 = function ( size ) {
THREE.BufferGeometry.call( this );
var verticesBuffer = new Float32Array( size * 3 );
var normalsBuffer = new Float32Array( size * 3 );
var uvsBuffer = new Float32Array( size * 2 );
this.vertices = [];
this.normals = [];
this.uvs = [];
for ( var i = 0; i < size; i ++ ) {
this.vertices.push( new THREE.TypedVector3( verticesBuffer, i * 3 ) );
this.normals.push( new THREE.TypedVector3( normalsBuffer, i * 3 ) );
this.uvs.push( new THREE.TypedVector2( uvsBuffer, i * 2 ) );
}
this.attributes[ 'position' ] = { array: verticesBuffer, itemSize: 3 };
this.attributes[ 'normal' ] = { array: normalsBuffer, itemSize: 3 };
this.attributes[ 'uv' ] = { array: uvsBuffer, itemSize: 2 };
};
THREE.Geometry5.prototype = Object.create( THREE.BufferGeometry.prototype );
THREE.TypedVector2 = function ( array, offset ) {
this.array = array;
this.offset = offset;
};
THREE.TypedVector2.prototype = Object.create( THREE.Vector2.prototype );
Object.defineProperties( THREE.TypedVector2.prototype, {
'x': {
get: function () { return this.array[ this.offset ]; },
set: function ( v ) { this.array[ this.offset ] = v; }
},
'y': {
get: function () { return this.array[ this.offset + 1 ]; },
set: function ( v ) { this.array[ this.offset + 1 ] = v; }
}
} );
THREE.TypedVector3 = function ( array, offset ) {
this.array = array;
this.offset = offset;
};
THREE.TypedVector3.prototype = Object.create( THREE.Vector3.prototype );
Object.defineProperties( THREE.TypedVector3.prototype, {
'x': {
get: function () { return this.array[ this.offset ]; },
set: function ( v ) { this.array[ this.offset ] = v; }
},
'y': {
get: function () { return this.array[ this.offset + 1 ]; },
set: function ( v ) { this.array[ this.offset + 1 ] = v; }
},
'z': {
get: function () { return this.array[ this.offset + 2 ]; },
set: function ( v ) { this.array[ this.offset + 2 ] = v; }
}
} );
I think I'm going to proceed with this one :)
The benchmark is not very fair at the moment because PlaneGeometry
is indexed and PlaneGeometry5
is not, but these are the results:
new THREE.PlaneGeometry( 200, 200, 200, 200 ); // ~370ms, 54Mb
new THREE.PlaneGeometry5( 200, 200, 200, 200 ); // ~100ms, 20Mb
I'll work on THREE.IndexedPlaneGeometry5
next.
@bhouston By the way, this is basically an improvement to Geometry
. I'm aiming to find something fairly user-friendly that sits on top of BufferGeometry
. For efficiency, one should probably deal with BufferGeometry
directly.
Also, would be nice to be able to do something like new THREE.Geometry( BufferGeometry )
to be able to manipulate a BufferGeometry
in an easy (albeit non performant) way.
Ok. Here's vs IndexedGeometry5
... :)
new THREE.PlaneGeometry( 200, 200, 200, 200 ); // ~370ms, 54Mb
new THREE.IndexedPlaneGeometry5( 200, 200, 200, 200 ); // ~24ms, 7Mb
For reference, this is the results by using plain BufferGeometry
:
new THREE.PlaneGeometryB( 200, 200, 200, 200 ); // ~8ms, 3Mb
@mrdoob Hehe. That's what I meant by "clever" the other day!
@WestLangley kudos to @jbaicoianu for the nudge ^^
Now the question is... should the primitive generators, and loaders use this interface? or leave this just for examples/tutorials and keep the internals as fast as possible?
Seems like I'm finally starting to experiment with something that I've been thinking for a long time.
The problem
The current
Geometry
structure is, albeit user-friendly, convoluted and unperformant. That structure was designed before WebGL even existed and it has been patched along the way.BufferGeometry
was created as a response to this. However,BufferGeometry
has the opposite problem, it's not user-friendly. I don't think a "normal" user needs to know what attributes are.Every time I see a three.js based project running on WebGL on mobile I hope (fear) the persons behind it didn't use
Geometry
.The solution
I spent a long while trying to think how to make
BufferGeometry
more user friendly, but I couldn't see a way of doing that without sacrificing its flexibility so eventually I decided to keepBufferGeometry
as it is. I spent some time during the last cycle makingProjector
(CanvasRenderer
,SoftwareRenderer
,SVGRenderer
, ...) support the structure instead.https://github.com/mrdoob/three.js/blob/dev/src/core/Projector.js#L307-L342
The intention now is to go with
Geometry2
, which eventually would replaceGeometry
and break backwards compatibility, although I wonder if people modify geometry much or not...The idea of
Geometry2
is to have everything in flat arrays:vertices
,normals
,uvs
andcolors
ready to be submitted to the GPU (on the WebGL side).https://github.com/mrdoob/three.js/blob/dev/src/core/Geometry2.js
However, that's not very user-friendly... I experimented with a similar class some months ago that had some "interfaces" to help the user:
https://github.com/mrdoob/three.js/blob/dev/examples/js/core/TypedGeometry.js
All that getters/setters black magic basically allows the user to modify the geometry without having to know that they're just modifying a blob of numbers. The API looked like this:
However, this would only be for the user. Generators and loaders would fill the arrays directly. Here's how
PlaneGeometry2
looks like:https://github.com/mrdoob/three.js/blob/dev/src/extras/geometries/PlaneGeometry2.js
As a side not, performance-wise, I've noticed that where
PlaneGeometry
was created in ~6ms,PlaneGeometry2
generates in ~0.3ms.On the
Projector
side, the code is looking pretty simple so far:https://github.com/mrdoob/three.js/blob/dev/src/core/Projector.js#L343-L358
To index or not to index
This is another topic that has troubled me for years and I think I want to bet to favour non-indexed geometries this time around. Part of the current over complexity in
WebGLRenderer
is the fact that WebGL limitsELEMENT_ARRAY_BUFFER
(indices) toUint16Array
(without extensions). This means that, internally,WebGLRenderer
has to split your geometry in chunks and well... I rather not explain how painful this is.So, even if indexed geometries save on GPU bandwidth, I don't think the complexity it creates is worth it.
Geometry2
won't support indices (Let me know if I'm missing something here).However,
BufferGeometry
will still be in place, so if someone wants to construct their custom indexed geometry they will still have a way to do it.Outcome
In theory, generators and loaders will speed up ~10x. Projects will consume less memory (single
Float32Array
s vs tons ofVector2
/Vector3
s). And, bothProjector
andWebGLRenderer
will get much simpler.