Reading instanced geometry

Hello,

I have just started using OpenNURBS to read 3dm models into my (C++) application. Naturally, getting started with a new library takes some time, but I am currently trying to figure out how to properly read the instanced geometry.
I am able to read non-instanced geometry, but the instances do not receive the correct transform; they remain at the origin. I started from this example: Rhino - Traversing Instance Definitions and used the m_xform in the ON_InstanceRef, but I must be doing something wrong. Does the transform need to be applied recursively, or is it already in world space?
And the geometry is not a problem, I am getting the geometry (although it is hard to tell whether I get the X instances, or only get the shape once, since they are not transformed.).

This is (a part of) my code for dealing with the instances:

void ProcessInstances(...)
{
    ONX_ModelComponentIterator it(model, ON_ModelComponent::Type::InstanceDefinition);
    for(ON_ModelComponentReference mcr = it.FirstComponentReference(); false == mcr.IsEmpty(); mcr = it.NextComponentReference())
    {
        const ON_ModelComponent* modelComponent = mcr.ModelComponent();
        if(CheckComponent(modelComponent, ...))
        {
            const ON_InstanceDefinition* idef = ON_InstanceDefinition::Cast(modelComponent);
            if(nullptr != idef)
            {
                ProcessInstanceDefinition(idef->Id(), ON_Xform::IdentityTransformation);
            }
        }
    }
}

void ProcessInstanceDefinition(const ON_UUID& idef_id, ON_Xform transform)
{
    const ON_ModelComponentReference& idef_component_ref = model.ComponentFromId(ON_ModelComponent::Type::InstanceDefinition, idef_id);
    const ON_InstanceDefinition* idef = ON_InstanceDefinition::Cast(idef_component_ref.ModelComponent());
    if(idef)
    {
        const ON_SimpleArray<ON_UUID>& geometry_id_list = idef->InstanceGeometryIdList();
        const int geometry_id_count = geometry_id_list.Count();
        for(int i = 0; i < geometry_id_count; i++)
        {
            const ON_ModelComponentReference& model_component_ref = model.ComponentFromId(ON_ModelComponent::Type::ModelGeometry, geometry_id_list[i]);
            const ON_ModelGeometryComponent* model_geometry = ON_ModelGeometryComponent::Cast(model_component_ref.ModelComponent());
            if(nullptr != model_geometry)
            {
                const ON_Geometry* geometry = model_geometry->Geometry(nullptr);
                if(nullptr != geometry)
                {
                    const ON_InstanceRef* iref = ON_InstanceRef::Cast(geometry);
                    if(iref)
                    {
                        ProcessInstanceDefinition(iref->m_instance_definition_uuid, iref->m_xform);
                    }
                    else
                    {
                        ProcessGeometryComponent(geometry, transform);
                    }
                }
            }
        }
    }
}

void ProcessGeometryComponent(const ON_Geometry* geom, ON_Xform transform)
{
    // Only handle BREPs for now... Other types later
    ON_Brep* brep = geom->BrepForm();
    if(brep)
    {
        brep->Transform(transform);
        brep->ShrinkSurfaces();
        int numFaces = brep->m_F.Count();
        for(int f = 0; f < numFaces; ++f)
        {
            // Process faces...
        }
    }
}

What I would like, is to just get all the geometry for now and instanced at the correct transform, I don’t need to use instancing in my application yet, and right now it is really about understanding the transforms OpenNURBS uses as well as the instancing mechanic.

EDIT: It seems that some transforms are being applied, but not all. I think it has to do with nesting (not sure what this is called in Rhino). So, for example, I have some instanced objects that are transformed into place w.r.t. their parent, but then this group (which is also instanced) is not transformed.

Thank you!

Nathaniël

Hi @nathaniel,

If you are using openNURBS to read a 3dm file into another application that does not support blocks or instances, then you can use this sample code as model of how to read ON_InstanceRef objects, find their instance definition geometries, and transform them to their world Cartesian location.

cmdSampleReadInstancesFromFile.cpp

Note, the sample just reads the stuff back into Rhino. But you should be able to adapt this to your application.

– Dale

Dale,

Thank you so much, this works like a charm, and straight out of the box!
This helps me out a lot!

Nathaniël

Hello, I recently encountered the same issue and it bothered me for a while. However, your code worked perfectly and I am very grateful!
But I have encountered a new problem, which is that for some instances, it reads the geometry model twice. One is read from the instance using your code, and it is located in the correct position. However, the geometry model that makes up the instance is also read by the Read function and often located in the wrong position, resulting in me reading the geometry model twice.
For complex geometry models, how can I robustly handle this situation? Or is there an obvious error in my processing method?
Looking forward to your prompt reply.

Allow me to elaborate further on my issue. When some elements in my model are combined into one or more instances while other elements are not, I encounter a problem. If I don’t handle these instances, my reading result is that the uninstanced elements are in the correct position, while the elements forming the instances are often in the wrong position.
However, if I follow the above cpp file to process the instances, my reading results show that the elements in the instances appear twice, and the “elements forming the instances are still in the wrong position”, although they also appear in the correct position.
Although the above cpp file can correctly process the position of the elements in the instances, it does not process the elements forming the instances (i.e. non-ON_InstanceRef classes). I think they should be deleted in some way, but I still have no clue. Believe me, I have been troubled by this issue for a long time.
If you could provide me with some ideas, I would be very grateful.

Hi @LXY,

When reading just geometry, you’ll want to skip over instance definition geometry.

A sample:

cmdSampleReadGeometryFromFile.cpp

– Dale

Thank you so much, I now know the function “IsInstanceDefinitionObject()” works! Thanks!