Jump to content

Symmetrical Topology Mapping

Recommended Posts


I have some models with a perfect symmetrical topology, all made of quads, but having a non-symmetrical pose. I wanted however to "symmetrize" them as much as I could to be able to resculpt them later using symmetry under ZBrush.

I have worked on a very weak algorithm (very long, can be optimized to reduce the number of tests along the tree), who is nevetheless working to assign to each point its "topological" symmetrical twin.

I wanted to share the code for people who might want to use it, and see what can come off from good coders :-)

There are several functions and the last one is the main one, and takes two points as input (an edge who is on the middle of the model, on its topological symmetrical plane). Here is the code, to be used in a point wrangle set on DETAILS :

// Function returning 1 if a point is in the list or 0 otherwise

function int isInArray(int pointnum; int listPoints[]) {
    int isinit = 0;
    foreach (int i; listPoints) {
        if (i == pointnum) {
            isinit = 1;
    return isinit;

// Function returning an array with two numbers of the prims that share an edge defined by two points Origin and Destination

function int[] findPrimFromEdge(int ptOrigin, ptDest) {

    int primsPtOrigin[] = pointprims(geoself(),ptOrigin);
    int primsPtDest[] = pointprims(geoself(),ptDest);
    int primsSiams[];
    int count = 0;

    foreach (int iterPrimOrigin; primsPtOrigin) {
        foreach (int iterPrimDest;primsPtDest) {
            if (iterPrimDest == iterPrimOrigin) {
                append(primsSiams, iterPrimDest);
            if (count==2) {
        if (count==2) {
    return primsSiams;


// Function returning an array with two point numbers that represent the opposed edge to the one sent as input, with the first point returned being connected to the Origin point of the edge sent as input

function int[] findOpposedEdge(int numprim, ptOrigin, ptDest) {
    int opposedEdge[];
    int tempOpposedEdge[];
    int pointListPrim[] = primpoints(geoself(),numprim);
    foreach (int iterPointPrim; pointListPrim) {
        if ((iterPointPrim != ptOrigin) && (iterPointPrim != ptDest)) {
                append(tempOpposedEdge, iterPointPrim);      
    if (pointedge(geoself(),tempOpposedEdge[0],ptOrigin) != -1) {
        append(opposedEdge, tempOpposedEdge[0]);
        append(opposedEdge, tempOpposedEdge[1]);
    else {
        append(opposedEdge, tempOpposedEdge[1]);
        append(opposedEdge, tempOpposedEdge[0]);
    return opposedEdge;

// Function returning the number of the prim adjacent to the prim sent as input and sharing an edge defined by its two points, and return -1 if empty in case of open surface

function int findAdjacentPrim(int numprim, ptOrigin, ptDest; int visitedPoints[]) {
    int adjprim = -1;
    int resultTest;
    int adjacentPrims[] = findPrimFromEdge(ptOrigin, ptDest);

    foreach (int iterprim; adjacentPrims) {
        if (iterprim != numprim) {
            adjprim = iterprim;
    return adjprim;

// Function that add the number of adjacent unvisited primitives to a list, and the id of the points of the shared edges with that prim to another list, from an input composed by a prim and an edge

function int addList(int primnumR, primnumL, ptOriginR, ptDestR, ptOriginL, ptDestL; int listOfPrimsR[], listOfPrimsL[], listOfPointsR[], listOfPointsL[], visPoints[], visPrims[]; int compteur; int compteurlist[]) {
    int oppEdgeR[] = findOpposedEdge(primnumR, ptOriginR, ptDestR);
    int oppEdgeL[] = findOpposedEdge(primnumL, ptOriginL, ptDestL);
    int ajout = 0;
    append(visPrims, primnumR);
    append(visPrims, primnumL);
    int adjPrimsR[];
    int adjPrimsL[];
    append(adjPrimsR, findAdjacentPrim(primnumR, oppEdgeR[0], oppEdgeR[1], visPoints));
    append(adjPrimsR, findAdjacentPrim(primnumR, ptOriginR, oppEdgeR[0], visPoints));
    append(adjPrimsR, findAdjacentPrim(primnumR, ptDestR, oppEdgeR[1], visPoints));
    append(adjPrimsL, findAdjacentPrim(primnumL, oppEdgeL[0], oppEdgeL[1], visPoints));
    append(adjPrimsL, findAdjacentPrim(primnumL, ptOriginL, oppEdgeL[0], visPoints));
    append(adjPrimsL, findAdjacentPrim(primnumL, ptDestL, oppEdgeL[1], visPoints));
    if ((adjPrimsR[0] != -1) && (adjPrimsR[0] != primnumL)) {
        if (isInArray(adjPrimsR[0], visPrims) == 0) {
            append(listOfPrimsR, adjPrimsR[0]);
            append(listOfPrimsL, adjPrimsL[0]);

            append(listOfPointsR, oppEdgeR[0]);
            append(listOfPointsR, oppEdgeR[1]);
            append(listOfPointsL, oppEdgeL[0]);
            append(listOfPointsL, oppEdgeL[1]);
            append(visPoints, oppEdgeR[0]);
            append(visPoints, oppEdgeR[1]);
            append(visPoints, oppEdgeL[0]);
            append(visPoints, oppEdgeL[1]);            
            append(compteurlist, compteur);
    if ((adjPrimsR[1] != -1) && (adjPrimsR[1] != primnumL)) {
        if (isInArray(adjPrimsR[1], visPrims) == 0) {
            append(listOfPrimsR, adjPrimsR[1]);
            append(listOfPrimsL, adjPrimsL[1]);

            append(listOfPointsR, ptOriginR);
            append(listOfPointsR, oppEdgeR[0]);
            append(listOfPointsL, ptOriginL);
            append(listOfPointsL, oppEdgeL[0]);
            append(visPoints, ptOriginR);
            append(visPoints, oppEdgeR[0]);
            append(visPoints, ptOriginL);
            append(visPoints, oppEdgeL[0]);            
            append(compteurlist, compteur);
    if ((adjPrimsR[2]!= -1) && (adjPrimsR[2] != primnumL)) {
        if (isInArray(adjPrimsR[2], visPrims) == 0) {
            append(listOfPrimsR, adjPrimsR[2]);
            append(listOfPrimsL, adjPrimsL[2]);

            append(listOfPointsR, ptDestR);
            append(listOfPointsR, oppEdgeR[1]);
            append(listOfPointsL, ptDestL);
            append(listOfPointsL, oppEdgeL[1]);
            append(visPoints, ptOriginR);
            append(visPoints, oppEdgeR[0]);
            append(visPoints, ptOriginL);
            append(visPoints, oppEdgeL[0]);            
            append(compteurlist, compteur);
    return ajout;


function void findTopology(int ptOriginMedian, ptDestMedian) {

    int listPtR[];
    int listPtL[];
    int listPrimR[];
    int listPrimL[];
    int visitedPoints[];
    int visitedPrims[];
    int primsStart[];
    int safeguard;
    int resulthandle;
    int countPrim;
    int counttreat = 0;
    int countlist[];
    int currentPtOrigR;
    int currentPtDestR;
    int currentPtOrigL;
    int currentPtDestL;
    int currentPrimR;
    int currentPrimL;
    int actif = 1;
    primsStart = findPrimFromEdge(ptOriginMedian,ptDestMedian);
    append(listPrimR, primsStart[0]);
    append(listPrimL, primsStart[1]);
    append(listPtR, ptOriginMedian);
    append(visitedPoints, ptOriginMedian);
    append(listPtL, ptOriginMedian);
    append(listPtR, ptDestMedian);
    append(visitedPoints, ptDestMedian);
    append(listPtL, ptDestMedian);
    safeguard = 0;
    while ((actif != 0) && safeguard < 200) {
        countPrim = len(listPrimR);
        actif = 0;
        for (int i=0; i < countPrim; i++) {
            if (isInArray(listPrimR,visitedPrims) == 0) {
                currentPrimR = listPrimR;
                currentPrimL = listPrimL;
                currentPtOrigR = listPtR[2*i];
                currentPtDestR = listPtR[2*i + 1];
                currentPtOrigL = listPtL[2*i];
                currentPtDestL = listPtL[2*i + 1];
                actif += addList(currentPrimR, currentPrimL, currentPtOrigR, currentPtDestR, currentPtOrigL, currentPtDestL, listPrimR, listPrimL, listPtR, listPtL, visitedPoints, visitedPrims, counttreat, countlist);
        // Safeguard to avoid endless loops
        printf("Loop: %d\n",safeguard);
    printf("ListPrimR : %d et ListPrimL : %d\n", len(listPrimR), len(listPrimL));
    printf("ListPointR : %d et ListPointL : %d\n", len(listPtR), len(listPtL));
    printf("Countlist : %d\n", len(countlist));
    for (int index = 0; index<len(listPtR); index++) {
        resulthandle = setpointattrib(geoself(),"symnum",listPtR[index],listPtL[index]);
        resulthandle = setpointattrib(geoself(),"symnum",listPtL[index],listPtR[index]);
        resulthandle = setpointattrib(geoself(),"traitement",listPtR[index],countlist[index]);
        resulthandle = setpointattrib(geoself(),"traitement",listPtL[index],countlist[index]);
        resulthandle = setpointattrib(geoself(),"Cd", listPtL[index], {1,0,0});
        resulthandle = setpointattrib(geoself(),"Cd",listPtR[index],{0,1,0});    

// LAUNCH !!

findTopology(8659, 2265);

And then the code to put on a point wrangle set to run on POINTS :

vector symP = point(geoself(),"P", i@symnum);
vector moyP;

if (@symnum == @ptnum) {
    @Cd = {0,0,1};
    @P.x = 0;
    @group_center = 1;
else {
    moyP.x = (@P.x - symP.x)/2;
    moyP.y = (@P.y + symP.y)/2;
    moyP.z = (@P.z + symP.z)/2;
    @P = moyP;

Hope this can be useful for people out there.



  • Like 1

Share this post

Link to post
Share on other sites

Hello Stepbtstepvfx,

You answered me on the french houdinimatic Discord, linking to here,
i'd need further explanation as i'm a newbie user and that i'm trying something that a part of your code may resolve, but don't know which is which.

So the main idea is to delete parts of a quad geometry, let's take for example a sphere : i want to delete all but keep the "lines" that follow the geo in the up axis (given by a polyframe).

I've made several tests with no luck, and basically something like adding primitives along the N vector (which is the up converted to N) would be cool but this is not working.
i have a geometry with only points and those points have their Normals pointing from each pt to the next pt in the Y direction following the geo which is i think a good starting base but after that i cannot create lines that would connect each point to the next in regards to this N vector. 

Need your magic ;)  ;)   
Thank you very much /.



Edited by lepetitnono

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now