Error Activating Voxels using GPU data & variable radius

Hi,

I am trying to use GVDB 1.1 within a simulation for collisions. I need dynamically updated topology, using data residing on the GPU.

For that, I was following the gFluidSurface example, but the grid does not seem to be correctly updated and I would like to know what might be the problem.

We are using CUDA 9.1, QuadroK4200 with latest driver, Visual Studio 2015, Windows 7 professional.
I have built and ran the example without any problems.

here is the relevant code:

void init() 
{
    mVolumeGVDB.SetDebug(false);
    mVolumeGVDB.SetVerbose(false);
    mVolumeGVDB.SetProfile(false, false);
    mVolumeGVDB.SetCudaDevice(GVDB_DEV_CURRENT);
    mVolumeGVDB.Initialize();
    mVolumeGVDB.Configure(3, 3, 3, 3, 5);
    mVolumeGVDB.SetVoxelSize(1.0f, 1.0f, 1.0f);
    mVolumeGVDB.DestroyChannels();
    mVolumeGVDB.SetChannelDefault(16, 16, 1);
    mVolumeGVDB.AddChannel(Channel::DENSITY, T_FLOAT, 1);
}

void updateUsingGPU(DataPtr &handle, float *pos, unsigned int n, float r)
{
    mVolumeGVDB.Clear();
    if (handle.usedNum != n) 
        mVolumeGVDB.AllocData(handle, n, sizeof(Vector3DF), false);
    mVolumeGVDB.SetDataGPU(handle, n, (CUdeviceptr)pos, 0, sizeof(Vector3DF));
    mVolumeGVDB.SetPoints(handle, DataPtr(), DataPtr());
    mVolumeGVDB.RebuildTopology(n, r, worldOrigin);
    mVolumeGVDB.FinishTopology(false,true);
    mVolumeGVDB.UpdateAtlas();
    int ptLen = 0;
    mVolumeGVDB.InsertPointsSubcell(4, n, r*2, worldOrigin, ptLen);
    mVolumeGVDB.GatherDensity(4, n, r, worldOrigin, ptLen, 0, -1);
    mVolumeGVDB.UpdateApron(0 , 3.0f);
}

void updateUsingCPU(DataPtr &handle, float *pos, unsigned int n, float r)
{
    mVolumeGVDB.Clear();
    if (handle.usedNum != n) 
        mVolumeGVDB.AllocData(handle, n, sizeof(Vector3DF), false);
    mVolumeGVDB.CommitData(handle, n, (char*)pos, 0, sizeof(Vector3DF));
    mVolumeGVDB.SetPoints(handle, DataPtr(), DataPtr());
    for (int i = 0; i < n; i++) 
    {
        Vector3DF v(((float*)pos)[3 * i], ((float*)pos)[3 * i + 1], ((float*)pos)[3 * i + 2]);
        mVolumeGVDB.ActivateSpace(v - worldOrigin);
    }
    mVolumeGVDB.FinishTopology();
    mVolumeGVDB.UpdateAtlas();
    int ptLen = 0;
    mVolumeGVDB.InsertPointsSubcell(4, n, r*2, worldOrigin, ptLen);
    mVolumeGVDB.GatherDensity(4, n, r, worldOrigin, ptLen, 0, -1);
    mVolumeGVDB.UpdateApron(0 , 3.0f);
}

void drawVoxels()
{
    Vector3DF bmin, bmax;
    Node* node;
    for (int lev = 0; lev < 5; lev++) 
    {
        int node_cnt = mVolumeGVDB.getNumNodes(lev);
        if (node_cnt <= 0) continue;
        for (int n = 0; n < node_cnt; n++) 
        {
            node = mVolumeGVDB.getNodeAtLevel(n, lev);
            bmin = mVolumeGVDB.getWorldMin(node);
            bmax = mVolumeGVDB.getWorldMax(node);
            myDrawBox(bmin+worldOrigin, bmax+worldOrigin);
        }
    }
}

the updateUsingCPU is being used for debugging only, and it is working fine, but I need to define a “worldOrigin” that translates all the points to the positive domain. (or the negative. As long as all the points are in the same octant, the grid is built correctly). Is this the correct behavior?

the updateUsingGPU is the main method that I want to use, but the voxels are incorrect.
It is building 1 voxel per level only. When worldOrigin is (0,0,0), all the bmin are (0,0,0).
This is the same result I would expect if all of my points where (0,0,0), as if the gpu data was invalid or uninitialized…
But I have printed out and confirmed that the data is correctly stored in the array, so I don’t know what else might be the source of the problem.
When worldOrigin is not (0,0,0), it still creates the same number of voxels, but they are translated each by a different amount.

A second question is, how should I update the topology if my points have each a different radius?

Thank you,
Vladimir

Leaving a reply here for future reference.

The topology is being correctly updated now. There are two things to be aware of:

  1. all points must reside in the same octant. I’m not sure if this is strictly necessary, but I am getting correct results only when I do this.

  2. The voxel size has a different behavior. While in the CPU version (1.0) I can freely use SetVoxelSize, in the GPU version (1.1) VoxelSize should be 1.0. The points must be scaled beforehand if you want coarser grids.

In the drawVoxels I was not properly accounting for this, so I was getting wrong visualization.

Also, in the updateUsingGPU I don’t need to call AllocData. After SetDataGPU, The handle will just hold a pointer to my data and information about the data size.

For my application, I need custom kernels, so I am not using InsertPointsSubcell or Gather Density (as for now).

here is the new code:

void init() 
{
	mVolumeGVDB.SetDebug(false);
	mVolumeGVDB.SetVerbose(false);
	mVolumeGVDB.SetProfile(false, false);
	mVolumeGVDB.SetCudaDevice(GVDB_DEV_CURRENT);
	mVolumeGVDB.Initialize();
	
	mVolumeGVDB.Configure(3, 3, 3, 3, LEAFRES);
	mVolumeGVDB.SetVoxelSize(1, 1, 1); //always 1
	mVolumeGVDB.DestroyChannels();
	mVolumeGVDB.SetChannelDefault(16, 16, 16);
	mVolumeGVDB.AddChannel(Channel::DENSITY, T_FLOAT, 0);
}

void updateUsingGPU(nvdb::DataPtr &handle, float *pos, unsigned int n) {
	mVolumeGVDB.SetDataGPU(handle, n, (CUdeviceptr)pos, 0, sizeof(nvdb::Vector3DF));
	mVolumeGVDB.SetPoints(handle, DataPtr(), DataPtr());
	mVolumeGVDB.RebuildTopology(n, 1, nvdb::Vector3DF(0, 0, 0)); //points are pre-transformed
	mVolumeGVDB.FinishTopology(false, true);
	mVolumeGVDB.UpdateAtlas();
}

void drawVoxels() 
{
	//the following loop gets only the leaf bricks' BB. 
	//A certain number of voxel lies inside the bricks, depending on the configuration
	const unsigned int numSubdivisions = pow(2, LEAFRES);
	nvdb::Vector3DF bmin, bmax;
	nvdb::Node* node;
	for (int lev = 0; lev < 5; lev++) 
	{
		int node_cnt = mVolumeGVDB.getNumNodes(lev);
		if (node_cnt <= 0) continue;
		for (int n = 0; n < node_cnt; n++)
		{
			node = mVolumeGVDB.getNodeAtLevel(n, lev);
			bmin = mVolumeGVDB.getWorldMin(node);
			bmax = mVolumeGVDB.getWorldMax(node);
			
			//transform the BB info back to the world position
			float xmin = bmin.x*mfScale + mfWorldOrigin;
			float ymin = bmin.y*mfScale + mfWorldOrigin;
			float zmin = bmin.z*mfScale + mfWorldOrigin;
			float xmax = bmax.x*mfScale + mfWorldOrigin;
			float ymax = bmax.y*mfScale + mfWorldOrigin;
			float zmax = bmax.z*mfScale + mfWorldOrigin;

			drawBrick(xmin,ymin,zmin,xmax,ymax,zmax,numSubdivisions);
		}
		//draw only the leaf bricks
		return;
	}	
}

when passing the mfWorldOrigin and the mfScale in the RebuildTopology parameters, I was not getting the correct results

This is helpful, thank you for posting. Would you mind also posting your drawBrick function?

[[EDIT: I can’t login or reset the password with the old account.]]

Sure, but I am not using anything fancy, just old OpenGL style to draw lines.
The viewport gets really cluttered if I actually draw each voxel completely, so I am just optionally drawing the voxel’s edges in the face of the brick.

For better performance, modern GL should be used instead.
Here is the core of the voxel’s drawing:

glBegin(GL_LINES);

//...

if (drawVoxels) for (unsigned int sub = 1; sub < voxelsSubdvision; sub++) 
{
    float param = (float)sub / (float)voxelsSubdvision;
    float v = min[0] * (1 - param) + max[0] * param;
    glVertex3f(v, min[1], min[2]);
    glVertex3f(v, min[1], max[2]);
    glVertex3f(v, max[1], min[2]);
    glVertex3f(v, max[1], max[2]);
    glVertex3f(v, min[1], min[2]);
    glVertex3f(v, max[1], min[2]);
    glVertex3f(v, min[1], max[2]);
    glVertex3f(v, max[1], max[2]);

    v = min[1] * (1 - param) + max[1] * param;
    glVertex3f(min[0], v, min[2]);
    glVertex3f(min[0], v, max[2]);
    glVertex3f(max[0], v, min[2]);
    glVertex3f(max[0], v, max[2]);
    glVertex3f(min[0], v, min[2]);
    glVertex3f(max[0], v, min[2]);
    glVertex3f(min[0], v, max[2]);
    glVertex3f(max[0], v, max[2]);

    v = min[2] * (1 - param) + max[2] * param;
    glVertex3f(min[0], min[1], v);
    glVertex3f(min[0], max[1], v);
    glVertex3f(max[0], min[1], v);
    glVertex3f(max[0], max[1], v);
    glVertex3f(min[0], min[1], v);
    glVertex3f(max[0], min[1], v);
    glVertex3f(min[0], max[1], v);
    glVertex3f(max[0], max[1], v);
}

//...

glEnd();

you can click the link of “use old account to log in” just under that big green button. Not sure why they updated the account registration.

I also struggled with logging in. Thank you for posting that information! It is very helpful.