spite / THREE.MeshLine

Mesh replacement for THREE.Line
MIT License
2.18k stars 380 forks source link

(Feature request)Please add a feature to update the vertices #60

Open Hari-KrishnanV opened 6 years ago

Hari-KrishnanV commented 6 years ago

Hi,

I am just trying to draw a thick line then add, update or remove points from it dynamically at the run time. just like this,

var line = new MeshLine();
var geo = new THREE.Geometry();
geo.vertices.push( new THREE.Vector3( -10, 10, 0 ) );
geo.vertices.push( new THREE.Vector3( 10, 10, 0 ) );
line.setGeometry( geo );

//Adding new vertices
var updatedGeo = new THREE.Geometry();
updatedGeo.vertices.push( new THREE.Vector3( -10, 10, 0 ) );
updatedGeo.vertices.push( new THREE.Vector3( 10, 10, 0 ) );
updatedGeo.vertices.push( new THREE.Vector3( 10, 15, 0 ) );
line.setGeometry( updatedGeo ); //This produces "Source too large error"

With the current implementation, I think it is not possible to add more vertices to the existing geometry( only two points are needed to draw a line, but I am trying to draw a line which will bend at different positions ). I don't know whether my assumptions are correct or not, but I think that the "MeshLine.prototype.process" function will check for the existence of a position attribute in the BufferGeometry and if it is already there, the function will just copy the vertices of the geometry passed to the setGeometry function to the existing position array. In this case we won't be able to add new vertices as it will throw a "Source too large error" from the "MeshLine.prototype.process" function.

I made a quick and dirty modification to the code just to fullfill my needs,

I copied "LineMesh.prototype.process" function to implement a new function "prepareGeometry" which will replace the existing BufferGeometry attributes with new one created using the geometry passed to the setGeometry function. Now "LineMesh.prototype.prepareGeometry" will be called from the setGeometry function instead of "LineMesh.prototype.process". Also I have added a "LineMesh.prototype.updateGeometry" function by modifying "LineMesh.prototype.setGeometry" which will update the existing vertices using the "LineMesh.prototype.process" method. The following code describes the changes made,

LineMesh.prototype.setGeometry = function( g, c ) {

    this.widthCallback = c;

    this.positions = [];
    this.counters = [];

    if( g instanceof THREE.Geometry ) {
        for( var j = 0; j < g.vertices.length; j++ ) {
            var v = g.vertices[ j ];
            var c = j/g.vertices.length;
            this.positions.push( v.x, v.y, v.z );
            this.positions.push( v.x, v.y, v.z );
            this.counters.push(c);
            this.counters.push(c);
        }
    }

    if( g instanceof THREE.BufferGeometry ) {
        // read attribute positions ?
    }

    if( g instanceof Float32Array || g instanceof Array ) {
        for( var j = 0; j < g.length; j += 3 ) {
            var c = j/g.length;
            this.positions.push( g[ j ], g[ j + 1 ], g[ j + 2 ] );
            this.positions.push( g[ j ], g[ j + 1 ], g[ j + 2 ] );
            this.counters.push(c);
            this.counters.push(c);
        }
    }

    //this.process();
    this.prepareGeometry();

}

LineMesh.prototype.updateGeometry = function( g, c ) {

    this.widthCallback = c;

    this.positions = [];
    this.counters = [];

    if( g instanceof THREE.Geometry ) {
        for( var j = 0; j < g.vertices.length; j++ ) {
            var v = g.vertices[ j ];
            var c = j/g.vertices.length;
            this.positions.push( v.x, v.y, v.z );
            this.positions.push( v.x, v.y, v.z );
            this.counters.push(c);
            this.counters.push(c);
        }
    }

    if( g instanceof THREE.BufferGeometry ) {
        // read attribute positions ?
    }

    if( g instanceof Float32Array || g instanceof Array ) {
        for( var j = 0; j < g.length; j += 3 ) {
            var c = j/g.length;
            this.positions.push( g[ j ], g[ j + 1 ], g[ j + 2 ] );
            this.positions.push( g[ j ], g[ j + 1 ], g[ j + 2 ] );
            this.counters.push(c);
            this.counters.push(c);
        }
    }

    this.process();

}

LineMesh.prototype.prepareGeometry = function() {

    var l = this.positions.length / 6;

    this.previous = [];
    this.next = [];
    this.side = [];
    this.width = [];
    this.indices_array = [];
    this.uvs = [];

    for( var j = 0; j < l; j++ ) {
        this.side.push( 1 );
        this.side.push( -1 );
    }

    var w;
    for( var j = 0; j < l; j++ ) {
        if( this.widthCallback ) w = this.widthCallback( j / ( l -1 ) );
        else w = 1;
        this.width.push( w );
        this.width.push( w );
    }

    for( var j = 0; j < l; j++ ) {
        this.uvs.push( j / ( l - 1 ), 0 );
        this.uvs.push( j / ( l - 1 ), 1 );
    }

    var v;

    if( this.compareV3( 0, l - 1 ) ){
        v = this.copyV3( l - 2 );
    } else {
        v = this.copyV3( 0 );
    }
    this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
    this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
    for( var j = 0; j < l - 1; j++ ) {
        v = this.copyV3( j );
        this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
        this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
    }

    for( var j = 1; j < l; j++ ) {
        v = this.copyV3( j );
        this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
        this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
    }

    if( this.compareV3( l - 1, 0 ) ){
        v = this.copyV3( 1 );
    } else {
        v = this.copyV3( l - 1 );
    }
    this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
    this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );

    for( var j = 0; j < l - 1; j++ ) {
        var n = j * 2;
        this.indices_array.push( n, n + 1, n + 2 );
        this.indices_array.push( n + 2, n + 1, n + 3 );
    }

    if ( !this.attributes ){

    }

    this.attributes = {
        position: new THREE.BufferAttribute( new Float32Array( this.positions ), 3 ),
        previous: new THREE.BufferAttribute( new Float32Array( this.previous ), 3 ),
        next: new THREE.BufferAttribute( new Float32Array( this.next ), 3 ),
        side: new THREE.BufferAttribute( new Float32Array( this.side ), 1 ),
        width: new THREE.BufferAttribute( new Float32Array( this.width ), 1 ),
        uv: new THREE.BufferAttribute( new Float32Array( this.uvs ), 2 ),
        index: new THREE.BufferAttribute( new Uint16Array( this.indices_array ), 1 ),
        counters: new THREE.BufferAttribute( new Float32Array( this.counters ), 1 )
    }

    this.geometry.addAttribute( 'position', this.attributes.position );
    this.geometry.addAttribute( 'previous', this.attributes.previous );
    this.geometry.addAttribute( 'next', this.attributes.next );
    this.geometry.addAttribute( 'side', this.attributes.side );
    this.geometry.addAttribute( 'width', this.attributes.width );
    this.geometry.addAttribute( 'uv', this.attributes.uv );
    this.geometry.addAttribute( 'counters', this.attributes.counters );

    this.geometry.setIndex( this.attributes.index );

}

LineMesh.prototype.process = function() {

    var l = this.positions.length / 6;

    this.previous = [];
    this.next = [];
    this.side = [];
    this.width = [];
    this.indices_array = [];
    this.uvs = [];

    for( var j = 0; j < l; j++ ) {
        this.side.push( 1 );
        this.side.push( -1 );
    }

    var w;
    for( var j = 0; j < l; j++ ) {
        if( this.widthCallback ) w = this.widthCallback( j / ( l -1 ) );
        else w = 1;
        this.width.push( w );
        this.width.push( w );
    }

    for( var j = 0; j < l; j++ ) {
        this.uvs.push( j / ( l - 1 ), 0 );
        this.uvs.push( j / ( l - 1 ), 1 );
    }

    var v;

    if( this.compareV3( 0, l - 1 ) ){
        v = this.copyV3( l - 2 );
    } else {
        v = this.copyV3( 0 );
    }
    this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
    this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
    for( var j = 0; j < l - 1; j++ ) {
        v = this.copyV3( j );
        this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
        this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
    }

    for( var j = 1; j < l; j++ ) {
        v = this.copyV3( j );
        this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
        this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
    }

    if( this.compareV3( l - 1, 0 ) ){
        v = this.copyV3( 1 );
    } else {
        v = this.copyV3( l - 1 );
    }
    this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
    this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );

    for( var j = 0; j < l - 1; j++ ) {
        var n = j * 2;
        this.indices_array.push( n, n + 1, n + 2 );
        this.indices_array.push( n + 2, n + 1, n + 3 );
    }

    if (!this.attributes) {
        this.attributes = {
            position: new THREE.BufferAttribute( new Float32Array( this.positions ), 3 ),
            previous: new THREE.BufferAttribute( new Float32Array( this.previous ), 3 ),
            next: new THREE.BufferAttribute( new Float32Array( this.next ), 3 ),
            side: new THREE.BufferAttribute( new Float32Array( this.side ), 1 ),
            width: new THREE.BufferAttribute( new Float32Array( this.width ), 1 ),
            uv: new THREE.BufferAttribute( new Float32Array( this.uvs ), 2 ),
            index: new THREE.BufferAttribute( new Uint16Array( this.indices_array ), 1 ),
            counters: new THREE.BufferAttribute( new Float32Array( this.counters ), 1 )
        }
    }
    else{
        this.attributes.position.copyArray(new Float32Array(this.positions));
        this.attributes.position.needsUpdate = true;
        this.attributes.previous.copyArray(new Float32Array(this.previous));
        this.attributes.previous.needsUpdate = true;
        this.attributes.next.copyArray(new Float32Array(this.next));
        this.attributes.next.needsUpdate = true;
        this.attributes.side.copyArray(new Float32Array(this.side));
        this.attributes.side.needsUpdate = true;
        this.attributes.width.copyArray(new Float32Array(this.width));
        this.attributes.width.needsUpdate = true;
        this.attributes.uv.copyArray(new Float32Array(this.uvs));
        this.attributes.uv.needsUpdate = true;
        this.attributes.index.copyArray(new Uint16Array(this.indices_array));
        this.attributes.index.needsUpdate = true;
    }

    this.geometry.addAttribute( 'position', this.attributes.position );
    this.geometry.addAttribute( 'previous', this.attributes.previous );
    this.geometry.addAttribute( 'next', this.attributes.next );
    this.geometry.addAttribute( 'side', this.attributes.side );
    this.geometry.addAttribute( 'width', this.attributes.width );
    this.geometry.addAttribute( 'uv', this.attributes.uv );
    this.geometry.addAttribute( 'counters', this.attributes.counters );

    this.geometry.setIndex( this.attributes.index );

}

In effect, we can call the "setGeometry" method if we wish to add new vertices or call the "updateGeometry" if we wish to update the existing vertices.

I know this is not the perfect implementation but it will be great if there is a feature to add, remove or update vertices.

kikohs commented 6 years ago

Interested in this as well.