|
|
@@ -171,6 +171,16 @@ class RenderObject {
|
|
|
*/
|
|
|
this.attributes = null;
|
|
|
|
|
|
+ /**
|
|
|
+ * An object holding the version of the
|
|
|
+ * attributes. The keys are the attribute names
|
|
|
+ * and the values are the attribute versions.
|
|
|
+ *
|
|
|
+ * @type {?Object<string, number>}
|
|
|
+ * @default null
|
|
|
+ */
|
|
|
+ this.attributesId = null;
|
|
|
+
|
|
|
/**
|
|
|
* A reference to a render pipeline the render
|
|
|
* object is processed with.
|
|
|
@@ -290,7 +300,7 @@ class RenderObject {
|
|
|
|
|
|
/**
|
|
|
* An event listener which is executed when `dispose()` is called on
|
|
|
- * the render object's material.
|
|
|
+ * the material of this render object.
|
|
|
*
|
|
|
* @method
|
|
|
*/
|
|
|
@@ -300,7 +310,23 @@ class RenderObject {
|
|
|
|
|
|
};
|
|
|
|
|
|
+ /**
|
|
|
+ * An event listener which is executed when `dispose()` is called on
|
|
|
+ * the geometry of this render object.
|
|
|
+ *
|
|
|
+ * @method
|
|
|
+ */
|
|
|
+ this.onGeometryDispose = () => {
|
|
|
+
|
|
|
+ // clear geometry cache attributes
|
|
|
+
|
|
|
+ this.attributes = null;
|
|
|
+ this.attributesId = null;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
this.material.addEventListener( 'dispose', this.onMaterialDispose );
|
|
|
+ this.geometry.addEventListener( 'dispose', this.onGeometryDispose );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -439,6 +465,7 @@ class RenderObject {
|
|
|
|
|
|
this.geometry = geometry;
|
|
|
this.attributes = null;
|
|
|
+ this.attributesId = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -458,9 +485,25 @@ class RenderObject {
|
|
|
const attributes = [];
|
|
|
const vertexBuffers = new Set();
|
|
|
|
|
|
+ const attributesId = {};
|
|
|
+
|
|
|
for ( const nodeAttribute of nodeAttributes ) {
|
|
|
|
|
|
- const attribute = nodeAttribute.node && nodeAttribute.node.attribute ? nodeAttribute.node.attribute : geometry.getAttribute( nodeAttribute.name );
|
|
|
+ let attribute;
|
|
|
+
|
|
|
+ if ( nodeAttribute.node && nodeAttribute.node.attribute ) {
|
|
|
+
|
|
|
+ // node attribute
|
|
|
+ attribute = nodeAttribute.node.attribute;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ // geometry attribute
|
|
|
+ attribute = geometry.getAttribute( nodeAttribute.name );
|
|
|
+
|
|
|
+ attributesId[ nodeAttribute.name ] = attribute.version;
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
if ( attribute === undefined ) continue;
|
|
|
|
|
|
@@ -472,6 +515,7 @@ class RenderObject {
|
|
|
}
|
|
|
|
|
|
this.attributes = attributes;
|
|
|
+ this.attributesId = attributesId;
|
|
|
this.vertexBuffers = Array.from( vertexBuffers.values() );
|
|
|
|
|
|
return attributes;
|
|
|
@@ -736,7 +780,27 @@ class RenderObject {
|
|
|
*/
|
|
|
get needsGeometryUpdate() {
|
|
|
|
|
|
- return this.geometry.id !== this.object.geometry.id;
|
|
|
+ if ( this.geometry.id !== this.object.geometry.id ) return true;
|
|
|
+
|
|
|
+ if ( this.attributes !== null ) {
|
|
|
+
|
|
|
+ const attributesId = this.attributesId;
|
|
|
+
|
|
|
+ for ( const name in attributesId ) {
|
|
|
+
|
|
|
+ const attribute = this.geometry.getAttribute( name );
|
|
|
+
|
|
|
+ if ( attribute === undefined || attributesId[ name ] !== attribute.id ) {
|
|
|
+
|
|
|
+ return true;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -814,6 +878,7 @@ class RenderObject {
|
|
|
dispose() {
|
|
|
|
|
|
this.material.removeEventListener( 'dispose', this.onMaterialDispose );
|
|
|
+ this.geometry.removeEventListener( 'dispose', this.onGeometryDispose );
|
|
|
|
|
|
this.onDispose();
|
|
|
|