Problem updating extruded geometry without replacing VboMesh


#1

Hi All,

I’m trying to dynamically update some geometry created by an extruded spline - or more specifically I’m trying to do it in a way that isn’t computationally so expensive that it grinds my app to a halt.

I’m creating the geometry using code borrowed from the Extrude sample, this runs during setup():

void myApp::createBody(){
    
    // circle helper function
    const auto createCircle = []( float radius, int segments ) {
        ci::Shape2d shape;
        shape.moveTo( ci::vec2( 1, 0 ) * radius );
        float inc = ( 2.0f * M_PI ) / (float) segments;
        for( float angle = 0.0f; angle <= M_PI * 2.0f; angle += inc ) {
            shape.lineTo( ci::vec2( cos( angle ), sin( angle ) ) * radius );
        }
        shape.lineTo( ci::vec2( 1, 0 ) * radius );
        return shape;
    };
    
    // set spline points as initial particle positions
    for (size_t i = 0; i < mParticles.size(); i++){
        mSplinePoints.push_back(mParticles[i]->getLocation());
    }
    
    // create the initial spline
    ci::BSpline3f spline = ci::BSpline3f(mSplinePoints, mSplinePoints.size() - 1, false, true);
    
    // create trimesh by extruding along spline
    ci::TriMesh source = ci::geom::ExtrudeSpline( createCircle( 0.25f, 9 ), // last segment normals / black faces
                                                 spline,
                                                 30 ).thickness( []( float t ) { return ci::smoothstep( 1.0f, 0.05f, t ); } );
    
    const auto shader = ci::gl::getStockShader( ci::gl::ShaderDef().color() );
    mBody = ci::gl::Batch::create( source, shader );
    
}

I have a vector of particles that are moving around ( these are the same particles I substituted in when initially creating the spline using the initial particle positions ). Is there a way to alter the ‘shape’ of the geometry without creating a new mesh every frame?
Currently I’m doing the following:

void myApp::updateBody(){
    
    // circle helper function
    const auto createCircle = []( float radius, int segments ) {
        ci::Shape2d shape;
        shape.moveTo( ci::vec2( 1, 0 ) * radius );
        float inc = ( 2.0f * M_PI ) / (float) segments;
        for( float angle = 0.0f; angle <= M_PI * 2.0f; angle += inc ) {
            shape.lineTo( ci::vec2( cos( angle ), sin( angle ) ) * radius );
        }
        shape.lineTo( ci::vec2( 1, 0 ) * radius );
        return shape;
    };
    
    // update the splinepoint locations with the current particle locations
    for (size_t i = 0; i < mParticles.size(); i++){
        mSplinePoints[i] = mParticles[i]->getLocation();
    }
    
    // rebuild the spline
    ci::BSpline3f spline = ci::BSpline3f(mSplinePoints, mSplinePoints.size() - 1, false, true);
    
    // rebuild the geometry
    ci::TriMesh source = ci::geom::ExtrudeSpline( createCircle( 0.25f, 9 ), // last segment normals / black faces
                                                 spline,
                                                 30 ).thickness( []( float t ) { return ci::smoothstep( 1.0f, 0.05f, t ); } );
    // create a new vbomesh
    auto newVboMesh = ci::gl::VboMesh::create(source);
    
    // replace the Batch's vbomesh with the newly generated one
    mBody->replaceVboMesh( newVboMesh );
    
}

As one would expect, this causes the app to grind to a halt ( from 60fps to ~4fps). This isn’t unexpected as creating new geometry every frame is hardly a good idea, but I’m drawing a blank regarding another solution. Perhaps this could be done using a geometry shader? I thought about trying to store a vector of pointers to the particle positions, but this didn’t really make sense as the mBody batch’s mesh has no inherent knowledge of the spline used to create it.

Any thoughts/ideas would be appreciated.

Craig


#2

Hi,

you’re recreating everything every frame, which isn’t necessary. At a minimum, store the shape and the spline once and use them over and over. It should also be possible to create the mesh once (as a VboMesh) and simply update the vertex positions. This should be easy if the number of vertices per “slice” (shape) stays the same, as well as the number of steps along the spline. Texture coordinates and indices do not have to be updated then. Vertex normals will probably have to be updated along with the vertices, but this is slightly more involved.

-Paul


#3

Hi Paul,

Thanks for responding. You can see what I’m trying to do here: https://twitter.com/IamCraigPickard/status/818177562874707969

Having the createCircle() method inside of the update was laziness on my part. However, and I guess this relates back to my original question - I have a series of particles that are connected to form a chain of sorts. I use these points to generate the BSpline, which in turn dictates the geometry of the extruded shape. Is it possible for me to update the update the vertex positions without recalculating the mesh which is dependent on the spline for it’s shape (the spline also needs to be updated because the particle positions are constantly changing)?


#4

@Craigson I’ll try to help flush out Paul’s suggestions.

I’d start by looking at VboMeshApp to get an idea how to have position data in a dynamic buffer and indices in a static one. You’ll want to start with just those two to keep things simple and come back to texture and normals once everything else is working.

You’re going to have two main tasks: build the geometry and update the positions.

For building the geometry, the elegant way would be to use ExtrudeSpline and have it copy the various attributes into buffers. That won’t help with the updating since it’s doing everything in the protected calculate method. So copy pasta starts sounding pretty reasonable. I’d build the initial geometry in a straight line and then try modifying the positions with a simple linear transformation. Once you get that working then you can look at transforming each slice based on the spline’s path.

It’ll be a little work.