Bool ConnectObjects( String prefix, const maxon::BaseArray<BaseObject*>& objects, ConnectOptions& options) { // Check if the object can successfully be allocatd for the // specified plugin id. BaseObject* test = BaseObject::Alloc(options.forcePluginId); if (!test) { GeDebugOut("Can not allocate plugin object " + String::IntToString(options.forcePluginId)); return false; } // Get the name of the object so we can use it later. options.forceName = test->GetName(); BaseObject::Free(test); if (!prefix.Content()) prefix = options.forceName + ": "; // Final result of the function. Bool success = true; // Create the ConnectionList for the objects. ConnectionList list; switch (options.connectMode) { case CMB_MODE_ALL: { const auto end = objects.End(); for (auto it=objects.Begin(); it != end; ++it) { for (auto jt=objects.Begin(); jt != end; ++jt) { if (*it == *jt) continue; if (list.HasConnection(*it, *jt)) continue; list.Append(Connection(*it, *jt)); } } break; } case CMB_MODE_NEIGHBOR: { const auto end = objects.End(); for (auto it=objects.Begin(); it != end; ++it) { // Find the nearest object. BaseObject* nearest = nullptr; Float delta; for (auto jt=objects.Begin(); jt != end; ++jt) { if (*it == *jt) continue; if (list.HasConnection(*it, *jt)) continue; Connection conn(*it, *jt); if (!nearest || conn.delta < delta) { nearest = *jt; delta = conn.delta; } } if (nearest) list.Append(Connection(*it, nearest)); } break; } case CMB_MODE_CHAIN: { options.radius = 0; options.maxConnections = 0; const auto first = objects.Begin(); for (auto it=objects.Begin(); it != objects.End(); ++it) { Bool isLast = (it + 1) == objects.End(); if (isLast && (options.closedChain && *it != *first)) { Connection conn(*it, *first); list.Append(conn); } else if (!isLast) { Connection conn(*it, *(it + 1)); list.Append(conn); } } break; } default: GeDebugOut(String(__FUNCTION__) + ": Invalid connectMode"); return false; } // Sort the list by distance. list.SortByDelta(); // This map contains the number of connections each // object already has to make sure no object exceeds the // maximum number if connections. maxon::Bool created = false; maxon::HashMap<C4DAtom*, Int32> map; // Iterate over all connections and establish them. const auto end = list.End(); for (auto it=list.Begin(); it != end; ++it) { if (options.radius > 0.000001 && it->delta > options.radius) continue; // Find and eventually initialize the entries in the map. auto entry1 = map.FindOrCreateEntry(it->obj1, created); if (created) entry1->GetValue() = 0; auto entry2 = map.FindOrCreateEntry(it->obj2, created); if (created) entry2->GetValue() = 0; // Determine if the maximum number of connections is reached. const Int32 nConn1 = entry1->GetValue(); const Int32 nConn2 = entry2->GetValue(); if (options.maxConnections > 0 && (nConn1 >= options.maxConnections || nConn2 >= options.maxConnections)) continue; // Create the force object. BaseObject* force = BaseObject::Alloc(options.forcePluginId); if (!force) { success = false; break; } // Update the parameters. force->SetParameter(FORCE_TYPE, options.forceType, DESCFLAGS_SET_0); force->SetParameter(FORCE_OBJECT_A, it->obj1, DESCFLAGS_SET_0); force->SetParameter(FORCE_OBJECT_B, it->obj2, DESCFLAGS_SET_0); force->SetBit(BIT_ACTIVE); force->SetName(prefix + it->obj1->GetName() + " - " + it->obj2->GetName()); ++entry1->GetValue(); ++entry2->GetValue(); options.output.Append(force); // Position the force object in between the two objects. Matrix mg; mg.off = (it->obj1->GetMg().off + it->obj2->GetMg().off) * 0.5; force->SetMl(mg); } return true; }
Bool ExecuteAutoConnect() { ConnectOptions options; Bool addDynamicsTag = false, inheritDynamicsTag = true; GetInt32(CMB_FORCE, options.forcePluginId); GetInt32(CMB_TYPE, options.forceType); GetInt32(CMB_MODE, options.connectMode); GetInt32(EDT_MAXCONN, options.maxConnections); GetFloat(EDT_RADIUS, options.radius); GetBool(CHK_CLOSED, options.closedChain); GetBool(CHK_ADDDYNAMICS, addDynamicsTag); GetBool(CHK_COMPOUND, inheritDynamicsTag); // Create an InExcludeData for the selection object. GeData ge_selection(CUSTOMGUI_INEXCLUDE_LIST, DEFAULTVALUE); auto selectionList = static_cast<InExcludeData*>( ge_selection.GetCustomDataType(CUSTOMGUI_INEXCLUDE_LIST)); if (!selectionList) return false; // Get the active document and object. BaseDocument* doc = GetActiveDocument(); if (!doc) return false; BaseObject* op = doc->GetActiveObject(); if (!op) return false; // Create the root object that will contain the connectors. AutoFree<BaseObject> root(BaseObject::Alloc(Onull)); if (!root) return false; // Function to create a dynamics tag. auto fAddDynamicsTag = [doc] (BaseObject* op, Int32 mode) { // Create a dynamics tag for the root object if it // does not already exist. BaseTag* dyn = op->GetTag(ID_RIGIDBODY); if (!dyn) { dyn = op->MakeTag(ID_RIGIDBODY); if (dyn) doc->AddUndo(UNDOTYPE_NEW, dyn); } // Update the parameters. if (dyn) { dyn->SetParameter(RIGID_BODY_HIERARCHY, mode, DESCFLAGS_SET_0); doc->AddUndo(UNDOTYPE_CHANGE_SMALL, dyn); } }; // This list will contain all objects that should be connected. // While collecting, create the dynamics tags. maxon::BaseArray<BaseObject*> objects; doc->StartUndo(); for (BaseObject* child=op->GetDown(); child; child=child->GetNext()) { objects.Append(child); if (addDynamicsTag && inheritDynamicsTag) fAddDynamicsTag(child, RIGID_BODY_HIERARCHY_COMPOUND); } if (addDynamicsTag && !inheritDynamicsTag) fAddDynamicsTag(op, RIGID_BODY_HIERARCHY_INHERIT); // If no objects where collected, quit already. if (objects.GetCount() <= 0) { doc->EndUndo(); doc->DoUndo(false); return true; } // Create the connection objects. ConnectObjects(op->GetName() + ": ", objects, options); // Fill the selection list and insert the objects. for (auto it=options.output.Begin(); it != options.output.End(); ++it) { (*it)->InsertUnderLast(root); doc->AddUndo(UNDOTYPE_NEW, *it); selectionList->InsertObject(*it, 0); } root->SetName(op->GetName() + ": " + root->GetName() + " (" + options.forceName + ")"); doc->InsertObject(root, nullptr, nullptr); doc->AddUndo(UNDOTYPE_NEW, root); // Create the selection object. if (selectionList->GetObjectCount() > 0) { BaseObject* selection = BaseObject::Alloc(Oselection); if (selection) { selection->SetParameter(SELECTIONOBJECT_LIST, ge_selection, DESCFLAGS_SET_0); selection->SetName(op->GetName() + ": " + options.forceName + " (" + selection->GetName() + ")"); doc->InsertObject(selection, nullptr, nullptr); doc->AddUndo(UNDOTYPE_NEW, selection); } ActiveObjectManager_SetMode(ACTIVEOBJECTMODE_OBJECT, false); doc->SetActiveObject(selection); } else doc->SetActiveObject(root); root.Release(); doc->EndUndo(); EventAdd(); return true; }
// go through every (child) object static Bool Recurse(HierarchyHelp *hh, BaseThread *bt, BaseObject *main, BaseObject *op, const Matrix &ml, Real srad, Real crad, LONG sub, Bool single) { // test if input object if polygonal if (op->GetType()==Opolygon) { BaseObject *tp = NULL; PolyInfo *pli = NULL; const Vector *padr = ToPoly(op)->GetPointR(); Vector pa,pb; LONG pcnt = ToPoly(op)->GetPointCount(),i,side,a=0,b=0; const CPolygon *vadr = ToPoly(op)->GetPolygonR(); LONG vcnt = ToPoly(op)->GetPolygonCount(); Matrix m; Neighbor n; // load names from resource String pstr = GeLoadString(IDS_ATOM_POINT); String estr = GeLoadString(IDS_ATOM_EDGE); // initialize neighbor class if (!n.Init(pcnt,vadr,vcnt,NULL)) return FALSE; // create separate objects // if this option is enabled no polygonal geometry is build - more parametric objects // are returned instead if (single) { for (i=0; i<pcnt; i++) { // alloc sphere primitive tp=BaseObject::Alloc(Osphere); if (!tp) return FALSE; // add phong tag if (!tp->MakeTag(Tphong)) return FALSE; tp->SetName(pstr+" "+LongToString(i)); // set object parameters BaseContainer *bc = tp->GetDataInstance(); bc->SetReal(PRIM_SPHERE_RAD,srad); bc->SetReal(PRIM_SPHERE_SUB,sub); // insert as last object under main tp->InsertUnderLast(main); // set position in local coordinates tp->SetRelPos(padr[i]*ml); } for (i=0; i<vcnt; i++) { // get polygon info for i-th polygon pli = n.GetPolyInfo(i); for (side=0; side<4; side++) { // only proceed if edge has not already been processed // and edge really exists (for triangles side 2 from c..d does not exist as c==d) if (pli->mark[side] || side==2 && vadr[i].c==vadr[i].d) continue; // alloc cylinder primitive tp=BaseObject::Alloc(Ocylinder); if (!tp) return FALSE; // add phong tag if (!tp->MakeTag(Tphong)) return FALSE; switch (side) { case 0: a=vadr[i].a; b=vadr[i].b; break; case 1: a=vadr[i].b; b=vadr[i].c; break; case 2: a=vadr[i].c; b=vadr[i].d; break; case 3: a=vadr[i].d; b=vadr[i].a; break; } tp->SetName(estr+" "+LongToString(pli->edge[side])); pa = padr[a]*ml; pb = padr[b]*ml; // set object parameters BaseContainer *bc = tp->GetDataInstance(); bc->SetReal(PRIM_CYLINDER_RADIUS,crad); bc->SetReal(PRIM_CYLINDER_HEIGHT,Len(pb-pa)); bc->SetReal(PRIM_AXIS,4); bc->SetLong(PRIM_CYLINDER_CAPS,FALSE); bc->SetLong(PRIM_CYLINDER_HSUB,1); bc->SetLong(PRIM_CYLINDER_SEG,sub); // place cylinder at edge center tp->SetRelPos((pa+pb)*0.5); // build edge matrix m.v3=!(pb-pa); RectangularSystem(m.v3,&m.v1,&m.v2); tp->SetRelRot(MatrixToHPB(m, tp->GetRotationOrder())); // insert as last object under main tp->InsertUnderLast(main); } } } else { // check if polygonal geometry has to be built tp = BuildPolyHull(ToPoly(op),ml,srad,crad,sub,hh->GetLOD(),&n,bt); if (tp) { tp->SetName(op->GetName()); tp->InsertUnderLast(main); // check if isoparm geometry has to be built if (hh->GetBuildFlags()&BUILDFLAGS_ISOPARM) { LineObject *ip = BuildIsoHull(ToPoly(op),ml,srad,crad,sub,hh->GetLOD(),&n,bt); // isoparm always needs to be set into a polygon object if (ip) tp->SetIsoparm(ip); } } } } for (op=op->GetDown(); op; op=op->GetNext()) if (!Recurse(hh,bt,main,op,ml*op->GetMl(),srad,crad,sub,single)) return FALSE; // check for user break return !bt || !bt->TestBreak(); }