markaren / threepp

C++20 port of three.js (r129)
MIT License
581 stars 50 forks source link

What is the correct way to update the point array of a 3D line, such as add extra points to the 3D line #226

Closed asmwarrior closed 4 months ago

asmwarrior commented 4 months ago

Hi, I have an object which has the type: threepp::Line

I created it with such code:

    auto longLineGeometry = threepp::BufferGeometry::create();
    std::vector<float> longLineVertices = {
        0, 0, 0,
        1, 1, 2,
        2, 3, 4,
        6, 8, 9
    };
    lineGeometry->setAttribute("position", threepp::FloatBufferAttribute::create(longLineVertices, 3));

    // Create the line object
    auto line2 = threepp::Line::create(lineGeometry, lineMaterial);
    line2->name = "line3d";
    scene->add(line2);

Now, I have to update the points of the line by adding new points, so I wrote such code below:

void MyFrame::OnButtonAddPointClicked(wxCommandEvent& event)
{
    auto scene = openGLCanvas->GetSceneObject();

    auto line = scene->getObjectByName("line3d");

    BufferGeometry* geometry = line->geometry();

    const auto position = geometry->getAttribute<float>("position");
    auto& array = position->array();
    array.push_back(50.0); // point.x
    array.push_back(50.0); // point.y
    array.push_back(50.0); // point.z

    position->needsUpdate();

    geometry->computeBoundingSphere();
}

I found that the above code does not change the 3d line. What I know is that the array has a field named count_, see here:

https://github.com/markaren/threepp/blob/258708f68cde3fbedbc8ae005e7eca1406fae4b0/include/threepp/core/BufferAttribute.hpp#L93-L102

But I don't see there is a way to modify the count_ value.

Any ideas?

Thanks.

BTW, is it possible to not use the getAttribute<float>, but instead use some kind of Vector3?

markaren commented 4 months ago

You need to replace the old geometry with a new one. Alternativly create a bigger list of points (with potentially unused data). The point is that you can not change the size of an existing geometry.

Edit: You should be able to use draw range (still fixed size), but this is untested territory.

asmwarrior commented 4 months ago

You need to replace the old geometry with a new one.

Thanks for the help, the below code works:

void MyFrame::OnButtonAddPointClicked(wxCommandEvent& event)
{
    auto scene = openGLCanvas->GetSceneObject();

    auto line = scene->getObjectByName("line3d");

    BufferGeometry* geometry = line->geometry();

    const auto position = geometry->getAttribute<float>("position");
    auto& array = position->array();
    array.push_back(50.0); // point.x
    array.push_back(50.0); // point.y
    array.push_back(50.0); // point.z

    geometry->setAttribute("position", FloatBufferAttribute::create(array, 3));

    position->needsUpdate();

    geometry->computeBoundingSphere();

    Refresh(false);
}

The main change to my previous code is that I have add a line:

geometry->setAttribute("position", FloatBufferAttribute::create(array, 3));

So, I believe the geometry's vertices get updated. I'm not quite sure how the internal code works, but It does work.

Alternativly create a bigger list of points (with potentially unused data). The point is that you can not change the size of an existing geometry.

Edit: You should be able to use draw range (still fixed size), but this is untested territory.

Thanks, I see you have ported the drawRange function in this library, I will try it later. Hopefully we could have a test code about this method.

asmwarrior commented 4 months ago

BTW, is it possible to not use the getAttribute<float>, but instead use some kind of Vector3?

About this question, I change the above code below:

void MyFrame::OnButtonAddPointClicked(wxCommandEvent& event)
{
    auto scene = openGLCanvas->GetSceneObject();

    auto line = scene->getObjectByName("line3d");

    BufferGeometry* geometry = line->geometry();

    const auto position = geometry->getAttribute<Vector3>("position");
    auto& array = position->array();
    array.push_back(Vector3(50.0, 50.0, 50.0)); // point

    geometry->setAttribute("position", FloatBufferAttribute::create(array, 3));

    position->needsUpdate();

    geometry->computeBoundingSphere();

    Refresh(false);
}

The build error is in the line:

geometry->setAttribute("position", FloatBufferAttribute::create(array, 3));

I believe the array is a std::vector<Vector3> &, but I'm not sure how to pass this variable to FloatBufferAttribute::create function call.

Thanks.

markaren commented 4 months ago

array for the "position" attribute is std::vector<float>.

If you want a one-liner, you could do array.insert(array.end(), {50, 50, 50});.

markaren commented 4 months ago

Thanks, I see you have ported the drawRange function in this library, I will try it later. Hopefully we could have a test code about this method.

See tube_geometry.cpp demo on dev. I added use of DrawRange there.

asmwarrior commented 4 months ago

array for the "position" attribute is std::vector<float>.

If you want a one-liner, you could do array.insert(array.end(), {50, 50, 50});.

OK, I see, thanks for the help!

Thanks, I see you have ported the drawRange function in this library, I will try it later. Hopefully we could have a test code about this method.

See tube_geometry.cpp demo on dev. I added use of DrawRange there.

OK, let me learn that code there. Thanks.