I have an idea for a topology addon for blender!

It uses vertex colors.

You paint black vertex colors on a vertex color layer and use that layer for producing the results.

Paint black dots all over the high poly mesh.

Then the addon will merge all contiguous black vertices into one black vertex.
After this is done all white or none black vertices are merged with the closest black vertex.

The result should be a lower polygon model.

If someone can write that plugin for blender, it could speed up the way manual retopology is done.

I tried writing it but failed, and now I feel ill.

You may try to adding your black marked dots to a vertex group or rather the opposite and use the Decimate modifier → Collapse : Ratop:0 – Vertex Group (collapse only)??

I have the addon working some what! there is a bug in it right now.

1 Like

It’s almost working plugin. Is this distance function correct.

    #takes two vectors origin
    def getDistance(self,p1,p2):

        ret= ((p2.x-p1.x)**2) + ((p2.y-p1.y)**2) + ((p2.z-p1.z)**2)
        ret=math.sqrt(ret)

        return ret

Here is some screen shots showing the work and result.

This one shows the work.

This oe shows the result. As can be seen the result is incorrect. Grouping and moving the vertices for the black dots is working correctly. Now I need to get correct shortest distance between black dots that have been already grouped.

Screenshot at 2022-08-22 09-20-15

It works, I found the bug and fixed it.

Here is the result of the sphere with dots, transfered into lowpoly model with the addon. A manual call to the merge by distance function was needed.

fixedBug

5 Likes

Hmm… congrates… would be interesting to use this on more complex geomtry…

I am working on making it fast enough to be usable. Having a hard time with it.

It might be fast enough now to be usable. Now I am adding blue lines, so lines can be drawn. I am testing it out. Seems to be working so far.

Here is a test result I did.

First, the work piece. It was vertex painted with blue lines. After this I installed the black dots ontop the blue lines for the intersecting areas. It took some time to do the painting.

It took about 30min to process the 620k faces.

As can be seen, there is errors in it.

2 Likes

Does it maybe work better on all quad topology? Or at least getting rid of the ngons? You might also need more topology, and maybe a little more care to make sure you don’t miss placing any black dots at any intersecting blue lines. That appears to be potentially responsible for one of the errors on the side of the head.

It’s not usable yet, but for what you’re trying to code and the fact you’ve only been working on it a couple of days, it’s pretty impressive how far you’ve gotten.

4 Likes

Based on the source code for the original addon, I created an addon that allows the vertex painting to be used for drawing vertices and edges. This works without moving or merging. It creates new mesh. Has bugs I need to fix first. Here is a test I did with it.

workArea1

workAreaBack

resultFront

resultBack

1 Like

It has much improved. It now has green dot placements. They only work with quads though. But the blue lines can be used for 3 vertex polygons.

It’s faster and the placement of vertices is more accurate. After I get things fixed up I plan on releasing. I don’t know when or where. I have not yet decided on a license for it. GPL maybe.

1 Like

All Blender add-ons (using Blender Python) have to be GPL licensed :slight_smile:

1 Like

So this is just a suggestion, but you may want to try it on more complex geometry with more typical retopologized density than you used int your earlier tests* to see if it’s still more efficient than retologizing by hand, before declaring your current workflow complete and release ready. You don’t have to, and you can always modify it later, but you may not see the adoption you want if it’s quite so meticulous and confusing to set up.

Something that adds vertices where the blue lines intersect, or programatically adds the black dots then turns them into vertices, might present a more user friendly experience? Might also be better to just make tris unsupported instead of requiring users to draw twice as many lines for quads. (At least, from the images with all those interconnected flower shapes, that looks like that’s the workflow). Looks like it would also make the final output harder to predict since there’s a lot more for the artist to keep track of while sketching over their model.

I’m not trying to be discouraging and I think what you’ve achieved is very clever. I’m just thinking that if a character has 5,000 vertices (which is pretty light), that’s currently 5,000 carefully placed dots, followed by maybe 10,000 or so edges, which for all quad characters that would need about two lines for each edge is 20,000 hand drawn lines that have to be drawn individually it looks like due to their shape (setting the mode to darken while drawing the blue lines would help ensure you don’t accidentally draw through the black dots which might relieve some of the work involved).

Also as @joseph said Blender’s licensing requires the addon to be GPL. I just want to clarify and add this is true whether it’s free or commercial, so you personally don’t have to distribute it for free (and if there were assets included, they wouldn’t have to be GPL), but the required GPL licensing means other people can (though they generally don’t).

* Your test before was for a very light weight, low poly character, far more low poly than is typical for characters. It was sensible for a test run, particularly given that it took a half hour to process the geometry, but not nearly enough for a typical production ready model. Subdividing it as a quick fix to that doesn’t solve the problem, as the more low poly something is before you subdivide it, the mushier and less detailed it will be after.

2 Likes

I’d like to let the source code out soon. The GPL is good.

Here is some tests.

The black dots create vertex. The green dots finds nearest 4 vertex to the green dot center point.
The red dot finds 3 vertex nearest to it.
The blue lines finds the black dots next to it. It can make edges, tri or quads.

Blue lines need thickness to it.

The addon is able to process 1mil polygons in 5 minutes. The processing time depends on the density and number of colors.

I am trying to release the plugin. Hopefully it can be tested and fixed. Or rewritten if needed.


bl_info = {
    "name": "Dot Retopo",
    "blender": (2, 80, 0),
    "category": "Object",
}

import math
import bpy
import bmesh


class ObjectDotRetopo(bpy.types.Operator):
    """Dot Retopo from vertex colors"""      # Use this as a tooltip for menu items and buttons.
    bl_idname = "dotretopo.dot_retopo"        # Unique identifier for buttons and menu items to reference.
    bl_label = "Dot Retopo (use vertex colors for new mesh)"         # Display name in the interface.
#    bl_options = {'REGISTER', 'UNDO'}  # Enable undo for the operator.
 

    def getdis(self,cord1,cord2):

#         thecord=(cord1-cord2).length #speed increase

        thecord= (cord2.x-cord1.x)**2 + (cord2.y-cord1.y)**2 + (cord2.z-cord1.z)**2
        thecord=math.pow(thecord,0.5)
        return thecord

    def sharevertex(self,poly,poly2):

        v1=poly.vertices[0]
        v2=poly.vertices[1]
        v3=poly.vertices[2]

        t1=poly2.vertices[0]
        t2=poly2.vertices[1]
        t3=poly2.vertices[2]

        if(v1==t1):
            return True

        if(v1==t2):
            return True

        if(v1==t3):
            return True

        if(v2==t1):
            return True

        if(v2==t2):
            return True

        if(v2==t3):
            return True

        if(v3==t1):
            return True

        if(v3==t2):
            return True

        if(v3==t3):
            return True


        return False

    #does poly2 share an edge with any poly in thelist
    def sharevertexList(self,themesh,thelist,thelistsize,poly2):

        scan=0
        while(scan<thelistsize):
            poly=themesh.data.polygons[thelist[scan]]

            v1=poly.vertices[0]
            v2=poly.vertices[1]
            v3=poly.vertices[2]

            t1=poly2.vertices[0]
            t2=poly2.vertices[1]
            t3=poly2.vertices[2]

            if(v1==t1):
                return True

            if(v1==t2):
                return True

            if(v1==t3):
                return True

            if(v2==t1):
                return True

            if(v2==t2):
                return True

            if(v2==t3):
                return True

            if(v3==t1):
                return True

            if(v3==t2):
                return True

            if(v3==t3):
                return True

            scan+=1


        return False


    #test if color green color with error correction at half value
    def testGreenHalf(self,col1):
        ret = True

        if(col1[0]>0.4):
            ret=False
        if(col1[1]<0.5):
            ret=False

        if(col1[2]>0.4):
            ret=False

        return ret



    #test if color blue color with error correction at half value
    def testBlueHalf(self,col1):
        ret = True

        if(col1[0]>0.3):
            ret=False
        if(col1[1]>0.3):
            ret=False

        if(col1[2]<0.5):
            ret=False

        return ret

    #test if color Red color with error correction
    def testRedHalf(self,col1):
        ret = True

        if(col1[0]<0.5):
            ret=False
        if(col1[1]>0.4):
            ret=False

        if(col1[2]>0.4):
            ret=False

        return ret


    #test if color Red color with error correction
    def testRed(self,col1):
        ret = True

        if(col1[0]<0.9):
            ret=False
        if(col1[1]>0.1):
            ret=False

        if(col1[2]>0.1):
            ret=False

        return ret


    #test if color green color with error correction
    def testGreen(self,col1):
        ret = True

        if(col1[0]>0.1):
            ret=False
        if(col1[1]<0.9):
            ret=False

        if(col1[2]>0.1):
            ret=False

        return ret


    #test if color blue color with error correction
    def testBlue(self,col1):
        ret = True

        if(col1[0]>0.1):
            ret=False
        if(col1[1]>0.1):
            ret=False

        if(col1[2]<0.9):
            ret=False

        return ret


    def testBlue2(self,col1):
        ret = True

        if(col1[0]>0.0):
            ret=False
        if(col1[1]>0.0):
            ret=False

        if(col1[2]<0.9):
            ret=False

        return ret


    #test if color black color with error correction at 0.4
    def testBlackHalf(self,col1):
        ret = True

        if(col1[0]>0.4):
            ret=False
        if(col1[1]>0.4):
            ret=False
        if(col1[2]>0.4):
            ret=False

        return ret



    #test if color black color with error correction
    def testBlack(self,col1):
        ret = True

        if(col1[0]>0.1):
            ret=False
        if(col1[1]>0.1):
            ret=False
        if(col1[2]>0.1):
            ret=False

        return ret


    def testPolyColor2(self,mesh,poly):

        ret=False


        color_layer=mesh.data.vertex_colors.active
        scanli=0
        while(scanli<len(poly.loop_indices)):
            v1=poly.loop_indices[scanli]
#            if(self.testBlack(color_layer.data[v1].color)==True):
            if(self.testBlackHalf(color_layer.data[v1].color)==True):
                ret=True
                break
            scanli+=1
                
        return ret

    def testPolyColorGreen(self,mesh,poly):


        ret=False
        color_layer=mesh.data.vertex_colors.active
        scanli=0
        while(scanli<len(poly.loop_indices)):
            v1=poly.loop_indices[scanli]
            if(self.testGreenHalf(color_layer.data[v1].color)==True):
#            if(self.testGreen(color_layer.data[v1].color)==True):
                ret=True
                break
            scanli+=1
                
        return ret


    def testPolyColorBlue(self,mesh,poly):


        ret=False
        color_layer=mesh.data.vertex_colors.active
        scanli=0
        while(scanli<len(poly.loop_indices)):
            v1=poly.loop_indices[scanli]
#            if(self.testBlueHalf(color_layer.data[v1].color)==True):
            if(self.testBlue(color_layer.data[v1].color)==True):

                ret=True
                break
            scanli+=1
                
        return ret


    def testPolyColorRed(self,mesh,poly):


        ret=False
        color_layer=mesh.data.vertex_colors.active
        scanli=0
        while(scanli<len(poly.loop_indices)):
            v1=poly.loop_indices[scanli]
            if(self.testRedHalf(color_layer.data[v1].color)==True):
#            if(self.testRed(color_layer.data[v1].color)==True):
                ret=True
                break
            scanli+=1
                
        return ret


    #closes polygon vertices at center.
    def closePoly(self,themesh,polysrc):

        mesh=themesh.data
        v1=polysrc.vertices[0]
        v2=polysrc.vertices[1]
        v3=polysrc.vertices[2]
        newop=mesh.vertices[v1].co
        newop=(newop+mesh.vertices[v2].co)/2
        newop=(newop+mesh.vertices[v3].co)/2
        
        mesh.vertices[v1].co=newop
        mesh.vertices[v2].co=newop
        mesh.vertices[v3].co=newop

        return

#randomily places polygon vertices ontop of polygon vertices.
    def movePolyTo(self,themesh,polysrc,polyfr):

        mesh=themesh.data

        v1=polysrc.vertices[0]
        v2=polysrc.vertices[1]
        v3=polysrc.vertices[2]

        t1=polyfr.vertices[0]
        t2=polyfr.vertices[1]
        t3=polyfr.vertices[2]

        cord=(mesh.vertices[t1].co+mesh.vertices[v1].co)/2
        mesh.vertices[t1].co=cord
        mesh.vertices[v1].co=cord
        cord=(mesh.vertices[t2].co+mesh.vertices[v2].co)/2
        mesh.vertices[t2].co=cord
        mesh.vertices[v2].co=cord
        cord=(mesh.vertices[t3].co+mesh.vertices[v3].co)/2
        mesh.vertices[t3].co=cord
        mesh.vertices[v3].co=cord


        return

    #outdated unused function
    #move entire group of vertices into one central location
    def closeGroup(self,thelist,themesh):

        if(thelist[0]==-1):
            return

        #find mid point of first face
        poly=themesh.data.polygons[thelist[0]]
        newod=(themesh.data.vertices[poly.vertices[0]].co + themesh.data.vertices[poly.vertices[1]].co + themesh.data.vertices[poly.vertices[2]].co )/3

        scan=1
        while(thelist[scan]!= -1):
            poly=themesh.data.polygons[thelist[scan]]

            #first find mid point of face
            newod2=(themesh.data.vertices[poly.vertices[0]].co+themesh.data.vertices[poly.vertices[1]].co+themesh.data.vertices[poly.vertices[2]].co)/3
   
            #find mid point of faces
            newod=(newod+newod2)/2

            scan+=1

        scan=0
        while(thelist[scan]!= -1):
            poly=themesh.data.polygons[thelist[scan]]
            themesh.data.vertices[poly.vertices[0]].co=newod
            themesh.data.vertices[poly.vertices[1]].co=newod
            themesh.data.vertices[poly.vertices[2]].co=newod
            scan+=1
#        themesh.data.update()
        
        

        return

    def testBlackNearEdge(self,themesh,poly,edgeTable):

        ret= -1

        scan=0
        while(scan<3):

            vert=themesh.data.vertices[poly.vertices[scan]]
            scanedge=0
            while(scanedge<9 and edgeTable[(vert.index*10)+scanedge]>=0):

                poly=themesh.data.polygons[edgeTable[(vert.index*10)+scanedge]]
                 
                if(self.testPolyColor2(themesh,poly)==True):
                    return poly.index

                scanedge+=1

            scan+=1
        


        return ret

    def testBlackNearEdge2(self,themesh,poly,bllist,edgeTable):

        ret= -1

        scan=0
        while(scan<3):

            vert=themesh.data.vertices[poly.vertices[scan]]
            scanedge=0
            while(scanedge<9 and edgeTable[(vert.index*10)+scanedge]>=0):

                poly=themesh.data.polygons[edgeTable[(vert.index*10)+scanedge]]
                 
                if(self.testPolyColor2(themesh,poly)==True):
#                if(bllist[poly.index]==True):
                    return poly.index

                scanedge+=1

            scan+=1
        

        return ret

    #finds the three closest black dots to red dots location given by thecord.
    #builds a list of them and returns it.
    def findRedDot(self,themesh,thecord,numbgroups,bco):
        #thecord is .co cordinate
        #bgroup is list of polygon group numbers. flags of polygons of themesh.
        #bco is precalcauted black group cordinates.


        if(numbgroups<3):
            return [0,1,2]
        
        ret= [0,1,2]

        scanlist=0
        scancords=0
        testcord1=themesh.data.polygons[0].center*0#create vertex data object
        while(scancords<3):


            testcord1[0]=bco[(scancords*3)]
            testcord1[1]=bco[(scancords*3)+1]
            testcord1[2]=bco[(scancords*3)+2]

            dis1=self.getdis(testcord1,thecord)
            scanlist=0
            while(scanlist<numbgroups):
                if(scanlist!=ret[scancords]):

                    testcord1[0]=bco[(scanlist*3)]
                    testcord1[1]=bco[(scanlist*3)+1]
                    testcord1[2]=bco[(scanlist*3)+2]

                    dis2=self.getdis(testcord1,thecord)
                
                    if(dis2<=dis1):
                        if(ret[0]!=scanlist and ret[1]!=scanlist and ret[2]!=scanlist):
                            ret[scancords]=scanlist
                            dis1=dis2
                            
                
                scanlist+=1

            scancords+=1

        
        return ret


    def findGreenDot2(self,themesh,thecord,bgroup,numbgroups,bco):
        #thecord is .co cordinate
        #bgroup is list of polygon group numbers.


        if(numbgroups<4):
            return [0,1,2,0]
        
        ret= [0,1,2,3]

        scanlist=0
        scancords=0
        testcord1=themesh.data.polygons[0].center*0#create vertex data object
        while(scancords<4):


            testcord1[0]=bco[(scancords*3)]
            testcord1[1]=bco[(scancords*3)+1]
            testcord1[2]=bco[(scancords*3)+2]

            dis1=self.getdis(testcord1,thecord)
            scanlist=0
            while(scanlist<numbgroups):
                if(scanlist!=ret[scancords]):

                    testcord1[0]=bco[(scanlist*3)]
                    testcord1[1]=bco[(scanlist*3)+1]
                    testcord1[2]=bco[(scanlist*3)+2]

                    dis2=self.getdis(testcord1,thecord)
                
                    if(dis2<=dis1):
                        if(ret[0]!=scanlist and ret[1]!=scanlist and ret[2]!=scanlist and ret[3]!=scanlist):
                            ret[scancords]=scanlist
                            dis1=dis2
                            
                
                scanlist+=1

            scancords+=1

        
        return ret
  

    #locate the corners of the dot lines
    def findBlueDot(self,thelist,blist,bllist,themesh,edgeTable):


        if(thelist[0]==-1):
            return

        cords=0#number already indexed.
        retlist=[-1,-1,-1,-1]#list of black dot groups numbers
                
    
        scan=0
        while(thelist[scan] != -1 and cords<4):#//find the two,three,four black dots
            poly=themesh.data.polygons[thelist[scan]]
            
            vert1=themesh.data.vertices[poly.vertices[0]]
            vert2=themesh.data.vertices[poly.vertices[1]]
            vert3=themesh.data.vertices[poly.vertices[2]]

            #test all faces of all three vertices
            scanvert=0
            while(edgeTable[(vert1.index*10)+scanvert] != -1 and scanvert<19 and cords<4):
                testpoly=themesh.data.polygons[edgeTable[(vert1.index*10)+scanvert]]

                
                if(self.testPolyColor2(themesh,testpoly)):
#                if(bllist[testpoly.index]==True):
                    if(self.testList(retlist,4,blist[testpoly.index])==False):
                        retlist[cords]=blist[testpoly.index]
                        cords+=1
                else:
                    ptest=self.testBlackNearEdge(themesh,testpoly,edgeTable)
#                    ptest=self.testBlackNearEdge2(themesh,testpoly,bllist,edgeTable)
                    if(self.testList(retlist,4,blist[ptest])==False):
                        retlist[cords]=blist[ptest]
                        cords+=1

                    
                scanvert+=1

            scanvert=0
            while(edgeTable[(vert2.index*10)+scanvert] != -1 and scanvert<19 and cords<4):
                testpoly=themesh.data.polygons[edgeTable[(vert2.index*10)+scanvert]]

                if(self.testPolyColor2(themesh,testpoly)):
#                if(bllist[testpoly.index]==True):
                    if(self.testList(retlist,4,blist[testpoly.index])==False):
                        retlist[cords]=blist[testpoly.index]
                        cords+=1
                else:
                    ptest=self.testBlackNearEdge(themesh,testpoly,edgeTable)
#                    ptest=self.testBlackNearEdge2(themesh,testpoly,bllist,edgeTable)
                    if(self.testList(retlist,4,blist[ptest])==False):
                        retlist[cords]=blist[ptest]
                        cords+=1

                scanvert+=1

            scanvert=0
            while(edgeTable[(vert3.index*10)+scanvert] != -1 and scanvert<19 and cords<4):
                testpoly=themesh.data.polygons[edgeTable[(vert3.index*10)+scanvert]]

                if(self.testPolyColor2(themesh,testpoly)):
#                if(bllist[testpoly.index]==True):
                    if(self.testList(retlist,4,blist[testpoly.index])==False):
                        retlist[cords]=blist[testpoly.index]
                        cords+=1
                else:
                    ptest=self.testBlackNearEdge(themesh,testpoly,edgeTable)
#                    ptest=self.testBlackNearEdge2(themesh,testpoly,bllist,edgeTable)
                    if(self.testList(retlist,4,blist[ptest])==False):
                        retlist[cords]=blist[ptest]
                        cords+=1


                scanvert+=1

            scan+=1

        

        return retlist


    def testList(self,thelist,listsize,val):
        scan=0
        while(scan<listsize):
            if(thelist[scan]==val):
                return True 
            scan+=1
        return False


    #should be unused
    def markasblack(self,themesh):
        thelist=[-1]*len(themesh.data.polygons)
        thelistscan=0

        for scan in themesh.data.polygons:
            if(self.testPolyColor2(themesh,scan)==True):
                thelist[thelistscan]=scan.index
                thelistscan+=1

        return thelist

    #should be unused.
    def markasblue(self,themesh):
        thelist=[-1]*len(themesh.data.polygons)
        thelistscan=0

        for scan in themesh.data.polygons:
            if(self.testPolyColorBlue(themesh,scan)==True):
                thelist[thelistscan]=scan.index
                thelistscan+=1

        return thelist



    #Direct lookup from edgeTable to find colored near polygons, contigous region.
    def groupPoly3(self,themesh,poly,edgeTable):

        thelist = [-1] * len(themesh.data.polygons)
        thelistsize=1
        thelist[0]=poly.index

        countpoly2=0
        scan=0

        while(scan<thelistsize):
            
            polytest=themesh.data.polygons[thelist[scan]]
            scanvert=0
            while(scanvert<3):
                scanind=0
                while(scanind<19 and edgeTable[(polytest.vertices[scanvert]*10)+scanind]>=0):
                    if(self.testList(thelist,thelistsize,edgeTable[(polytest.vertices[scanvert]*10)+scanind])==False):
                        if(self.testPolyColor2(themesh,themesh.data.polygons[edgeTable[(polytest.vertices[scanvert]*10)+scanind]])==True):
                            thelist[thelistsize]=edgeTable[(polytest.vertices[scanvert]*10)+scanind]
                            thelistsize+=1
                    scanind+=1
                scanvert+=1

            scan+=1
#        print(thelistsize)
        return thelist



    def groupBluePoly(self,themesh,poly,edgeTable):

        thelist = [-1] * len(themesh.data.polygons)
        thelistsize=1
        thelist[0]=poly.index

        countpoly2=0
        scan=0
        added=True
        lastlistsize=0
        while(scan<thelistsize):
            polytest=themesh.data.polygons[thelist[scan]]
            scanvert=0
#            print(thelistsize)         
            while(scanvert<3):
                scanind=0
                while(scanind<19 and edgeTable[(polytest.vertices[scanvert]*10)+scanind]>=0):
                    if(self.testList(thelist,thelistsize,edgeTable[(polytest.vertices[scanvert]*10)+scanind])==False):
                        if(self.testPolyColorBlue(themesh,themesh.data.polygons[edgeTable[(polytest.vertices[scanvert]*10)+scanind]])==True):
                            thelist[thelistsize]=edgeTable[(polytest.vertices[scanvert]*10)+scanind]
                            thelistsize+=1
                            added=True
                    scanind+=1
                scanvert+=1

            scan+=1
        return thelist

    def groupGreenPoly(self,themesh,poly,edgeTable):

        thelist = [-1] * len(themesh.data.polygons)
        thelistsize=1
        thelist[0]=poly.index

        countpoly2=0
        scan=0
        added=True
        lastlistsize=0
        while(scan<thelistsize):
            polytest=themesh.data.polygons[thelist[scan]]
            scanvert=0
#            print(thelistsize)         
            while(scanvert<3):
                scanind=0
                while(scanind<19 and edgeTable[(polytest.vertices[scanvert]*10)+scanind]>=0):
                    if(self.testList(thelist,thelistsize,edgeTable[(polytest.vertices[scanvert]*10)+scanind])==False):
                        if(self.testPolyColorGreen(themesh,themesh.data.polygons[edgeTable[(polytest.vertices[scanvert]*10)+scanind]])==True):
                            thelist[thelistsize]=edgeTable[(polytest.vertices[scanvert]*10)+scanind]
                            thelistsize+=1
                            added=True
                    scanind+=1
                scanvert+=1

            scan+=1
        return thelist


    def groupRedPoly(self,themesh,poly,edgeTable):

        thelist = [-1] * len(themesh.data.polygons)
        thelistsize=1
        thelist[0]=poly.index

        countpoly2=0
        scan=0
        added=True
        lastlistsize=0
        while(scan<thelistsize):
            polytest=themesh.data.polygons[thelist[scan]]
            scanvert=0
#            print(thelistsize)         
            while(scanvert<3):
                scanind=0
                while(scanind<19 and edgeTable[(polytest.vertices[scanvert]*10)+scanind]>=0):
                    if(self.testList(thelist,thelistsize,edgeTable[(polytest.vertices[scanvert]*10)+scanind])==False):
                        if(self.testPolyColorRed(themesh,themesh.data.polygons[edgeTable[(polytest.vertices[scanvert]*10)+scanind]])==True):
                            thelist[thelistsize]=edgeTable[(polytest.vertices[scanvert]*10)+scanind]
                            thelistsize+=1
                            added=True
                    scanind+=1
                scanvert+=1

            scan+=1
        return thelist



    def buildedgeTable(self,themesh):

        edgeTable=[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1] * len(themesh.data.vertices)

        scan1=0
        while(scan1<len(themesh.data.polygons)):
            poly=themesh.data.polygons[scan1]
            scan2=0
            ind=0
            scan3=0
            while(scan3<3):
                scan2=0
                while(scan2<19 and edgeTable[(10*poly.vertices[scan3])+scan2]>=0):
                    scan2+=1
                edgeTable[(10*poly.vertices[scan3])+scan2]=scan1
                if(scan2>=19):
                    print('edge table entry full')
                scan3+=1

            scan1+=1


        return edgeTable


    def getgroupcord(self,themesh,bgroup):

        ret=[0.0,0.0,0.0]

        if(bgroup[0]==-1):
            return ret
        
        ret=themesh.data.polygons[bgroup[0]].center

        scan=1
        while(bgroup[scan]!=-1):
#            if(ret==[0,0,0]):   
 #               ret=themesh.data.polygons[bgroup[scan]].center
  #          else:
            ret=(ret+themesh.data.polygons[bgroup[scan]].center)*0.5

            scan+=1

        return ret

    def getbgroupcord(self,themesh,bgroup,num):

        ret=[0,0,0]
        scan=0
        while(scan<len(themesh.data.polygons)):

            if(bgroup[scan]==num):
                if(ret==[0,0,0]):
                    ret=themesh.data.polygons[scan].center
                else:
                    ret=(ret+themesh.data.polygons[scan].center)*0.5
    
            scan+=1
        
        return ret


    #build the mesh with collected data.
    def buildNewMesh(self,themesh,bgroup,bgnum,bco,blgroup,blgroupsize,grgroup,grgroupnum,rgroup,rgroupnum):
    #themesh is the active object in context of blender
    #bgroup is the black group polygons flag list
    #bgnum is number of black groups found
    #bco is the precalculated black group cordinates
    #blgroup is the blue indexes of the black groups for the blue polygons areas
    #blgroupsize is the numver of blgroups
    #grgroup is the green vertex lookup for the green groups dots
    #grgroupnum is the number of green dots or groups.

        verti= [-1] * (bgnum+1)
        vertidx=0

        bmn=bmesh.new()

        print('creating vertices in new mesh')
        scanbg=0
        cord=self.getbgroupcord(themesh,bgroup,0)#its for creating the vertex cord storage class
        while(scanbg<bgnum):

            #cycle though bgroup collecting cordinate to use.
#            cord=self.getbgroupcord(themesh,bgroup,scanbg)
            cord[0]=bco[(3*scanbg)]
            cord[1]=bco[(3*scanbg)+1]
            cord[2]=bco[(3*scanbg)+2]
            vert=bmesh.ops.create_vert(bmn,co=cord)
            verti[vertidx]=scanbg
            vertidx+=1
            bmn.verts.ensure_lookup_table()            
            scanbg+=1

#        bmn.verts.ensure_lookup_table()

        print('creating faces from blue')
        scanbl=0

        while(scanbl<blgroupsize):
#            print(scanbl)

#            bmn.ensure_lookup_table()
#            bmn.types.BMEdgeSeq.ensure_lookup_table()
#            bmesh.types.BMEdgeSeq.ensure_lookup_table(bmn)
            if(blgroup[scanbl+1]>=0):
#                if(blgroup[scanbl]>=0):
 #                   vertlist=bmn.verts[blgroup[scanbl]]
                if(blgroup[scanbl+1]>=0):
                    vertlist=[bmn.verts[blgroup[scanbl]],bmn.verts[blgroup[scanbl+1]]]
                if( blgroup[scanbl+2] >= 0):
                    vertlist=[ bmn.verts[blgroup[scanbl]],bmn.verts[blgroup[scanbl+1]],bmn.verts[blgroup[scanbl+2]] ]
                if(blgroup[scanbl+3]>=0):
                    vertlist=[ bmn.verts[blgroup[scanbl]],bmn.verts[blgroup[scanbl+1]],bmn.verts[blgroup[scanbl+2]],bmn.verts[blgroup[scanbl+3]] ]
                bmesh.ops.contextual_create(bmn,geom=vertlist)
#            else:
#                print('error in createblue face, only one vertex')
#                print(blgroup[scanbl])
#                print(blgroup[scanbl+1])
#                print(blgroup[scanbl+2])
#                print(blgroup[scanbl+3])

            scanbl+=4


        print('creating faces from green')
        scangr=0
        while(scangr<grgroupnum):
  #          print(scangr)
 #           print(grgroupnum)
            if(grgroup[scangr]>=0 and grgroup[scangr+1]>=0 and grgroup[scangr+2]>=0 and grgroup[scangr+3]>=0):

                vertlist=[ bmn.verts[grgroup[scangr]] ,bmn.verts[grgroup[scangr+1]],bmn.verts[grgroup[scangr+2]],bmn.verts[grgroup[scangr+3]] ]
#                print(vertlist)
                bmesh.ops.contextual_create(bmn,geom=vertlist)
            else:
                print('error in create green, error 1')
            scangr+=4


        print('creating faces from Red')
        scanr=0
        while(scanr<rgroupnum):
  #          print(scangr)
 #           print(grgroupnum)
            if(rgroup[scanr]>=0 and rgroup[scanr+1]>=0 and rgroup[scanr+2]>=0):

                vertlist=[ bmn.verts[rgroup[scanr]] ,bmn.verts[rgroup[scanr+1]],bmn.verts[rgroup[scanr+2]] ]
#                print(vertlist)
                bmesh.ops.contextual_create(bmn,geom=vertlist)
            else:
                print('error in create Red, error 1')
            scanr+=3



        bpy.ops.object.add(type='MESH')
        bmn.to_mesh(bpy.context.active_object.data)
        bmn.free()

        return


#close in the dots function
    def closeDots(self,themesh):
       
        mesh = themesh.data

        #build edge lookup table for line and dot detection        
        edgeTable=self.buildedgeTable(themesh)

        #build black polygon table.

        blacklist=[False] * len(themesh.data.polygons)#small speed increase for blue lines

        usedbpoly=[False] * len(themesh.data.polygons)
        bgroup= [-1] * len(themesh.data.polygons)
        bgroupnum=0
        
        print('build black dot groups and index')
        scanpoly=0
        while(scanpoly<len(themesh.data.polygons)):

            apoly=themesh.data.polygons[scanpoly]
            if(usedbpoly[apoly.index]==False):
                if(self.testPolyColor2(themesh,apoly)==True):
                    tlist=self.groupPoly3(themesh,apoly,edgeTable)
                    listscan=0
                    while(tlist[listscan]!=-1):
                        usedbpoly[themesh.data.polygons[tlist[listscan]].index]=True
                        bgroup[themesh.data.polygons[tlist[listscan]].index]=bgroupnum
                        blacklist[listscan]=True
                        listscan+=1
                    bgroupnum+=1
#                    print(bgroupnum)

            scanpoly+=1
#        print(bgroupnum)


        print('build green lookup index')
#        listblcords=self.listcordsbgroup(themesh,bgroup,bgroupnum)

        print('build black group cordinate list')
        #this is used for speed increase, to precalcuate the cordinates for vertex
        bgroupco=[0.0,0.0,0.0] * (bgroupnum+1)
        scanco=0
        while(scanco<bgroupnum):
 #           print(scanco)
#            print(bgroupnum)
            acord=self.getbgroupcord(themesh,bgroup,scanco)
            bgroupco[scanco*3]=acord[0]
            bgroupco[(scanco*3)+1]=acord[1]
            bgroupco[(scanco*3)+2]=acord[2]
            scanco+=1

        usedgrpoly=[False] * len(themesh.data.polygons)
        grgroup= [-1,-1,-1,-1] * len(themesh.data.polygons)#that is a dot list
        grgroupnum=0        
        print('build green poly groups lookup index')
        scanpoly=0
        while(scanpoly<len(themesh.data.polygons)):
            apoly=themesh.data.polygons[scanpoly]
            if(usedgrpoly[apoly.index]==False):
                if(self.testPolyColorGreen(themesh,apoly)==True):

  #                  print('group the green poly list')
                    tlist=self.groupGreenPoly(themesh,apoly,edgeTable)

 #                   print('sort the list')
                    listscan=0
                    while(tlist[listscan]!= -1):
                        usedgrpoly[tlist[listscan]]=True
                        listscan+=1                    

#                    print('find the black dots for green poly')
                    grcord=self.getgroupcord(themesh,tlist)
#                    dotlist=self.findGreenDot(themesh,grcord,bgroup,bgroupnum)
                    dotlist=self.findGreenDot2(themesh,grcord,bgroup,bgroupnum,bgroupco)

                
                    grgroup[grgroupnum]=dotlist[0]
                    grgroup[grgroupnum+1]=dotlist[1]
                    grgroup[grgroupnum+2]=dotlist[2]
                    grgroup[grgroupnum+3]=dotlist[3]
                    grgroupnum+=4
 #                   print(grgroupnum/4)



#                    print(listscan)

            scanpoly+=1

        
        #build the red dot face lookup table.
        usedrpoly=[False] * len(themesh.data.polygons)
        rgroup= [-1,-1,-1] * len(themesh.data.polygons)#that is a dot list
        rgroupnum=0        
        print('build red poly groups lookup index')
        scanpoly=0
        while(scanpoly<len(themesh.data.polygons)):
            apoly=themesh.data.polygons[scanpoly]
            if(usedrpoly[apoly.index]==False):
                if(self.testPolyColorRed(themesh,apoly)==True):

  #                  print('group the green poly list')
                    tlist=self.groupRedPoly(themesh,apoly,edgeTable)

 #                   print('sort the list')
                    listscan=0
                    while(tlist[listscan]!= -1):
                        usedrpoly[tlist[listscan]]=True
                        listscan+=1                    

#                    print('find the black dots for green poly')
                    rcord=self.getgroupcord(themesh,tlist)
#                    dotlist=self.findGreenDot(themesh,grcord,bgroup,bgroupnum)
                    dotlist=self.findRedDot(themesh,rcord,bgroupnum,bgroupco)

                
                    rgroup[rgroupnum]=dotlist[0]
                    rgroup[rgroupnum+1]=dotlist[1]
                    rgroup[rgroupnum+2]=dotlist[2]
#                    rgroup[rgroupnum+3]=dotlist[3]
                    rgroupnum+=3
 #                   print(grgroupnum/4)



#                    print(listscan)

            scanpoly+=1



        

        #build blue lookup table with connected black polygons
        usedblpoly=[False] * len(themesh.data.polygons)
        blgroup= [-1,-1,-1,-1] * len(themesh.data.polygons)#that is a dot list
        blgroupnum=0        

        print('build blue line lookup index')
        scanpoly=0
        while(scanpoly<len(themesh.data.polygons)):
            apoly=themesh.data.polygons[scanpoly]
            if(usedblpoly[apoly.index]==False):
                if(self.testPolyColorBlue(themesh,apoly)==True):
   #                 print('group the blue poly list')
                    tlist=self.groupBluePoly(themesh,apoly,edgeTable)
    #                print('find the black dots')
                    dotlist=self.findBlueDot(tlist,bgroup,blacklist,themesh,edgeTable)
#                    print(dotlist)
     #               print('sort the list')
                    listscan=0
                    while(tlist[listscan]!= -1):
                        usedblpoly[tlist[listscan]]=True
                        listscan+=1                    
#                    countlistnum=0

                    blgroup[blgroupnum]=dotlist[0]
                    blgroup[blgroupnum+1]=dotlist[1]
                    blgroup[blgroupnum+2]=dotlist[2]
                    blgroup[blgroupnum+3]=dotlist[3]
                    blgroupnum+=4
#                    print(blgroupnum/4)


#                    print(listscan)

            scanpoly+=1
        

        #build the new bmesh object with the collected data
        print('build new mesh')
        self.buildNewMesh(themesh,bgroup,bgroupnum,bgroupco,blgroup,blgroupnum,grgroup,grgroupnum,rgroup,rgroupnum)


        print('program finished')
        return



    def execute(self, context):
        self.closeDots(context.active_object)
        return {'FINISHED'} 


def menu_func(self, context):
    self.layout.operator(ObjectDotRetopo.bl_idname)

def register():
    bpy.utils.register_class(ObjectDotRetopo)
    bpy.types.VIEW3D_MT_object.append(menu_func)  # Adds the new operator to an existing menu.

def unregister():
    bpy.utils.unregister_class(ObjectDotRetopo)


if __name__ == "__main__":
    register()

I don’t know where I can upload the archive at. Here is the manual for it.

Version 1.0

Dot topology is an addon plugin script for blender. Released under same license as blender. Dot topology works by painting vertices in blender.

White is white space, it’s ignored by script.
black is a vertex. A vertex is created for it’s center point. The idea is to create black dots.
Red is a tri. It looks for the nearest three vertex, to create a polygon. The idea is placing a red dot in middle of black dots.
Green is a quad. It looks for nearest four vertex, to create polygons from. The idea is placing green dot in middle of black dots.
Blue is for connecting black dots. It can make edge, tri, or quad. Idea connecting black dots with blue.
Blue is limited, it needs good enough line thickness to work, and good enough contact with black dots.

1 Like

You should make a github repo so you can have a proper readme/wiki with markdown, versioning, and people can easily report issues :slight_smile:

1 Like

Yep, either GitHub and then linking to it from the Released Scripts and Themes section on BA if you want to do things the traditional way, or uploading the .py or zip file directly in Released Scripts and Themes if you don’t want to bother with GitHub yet.

You could also put it on Gumroad as “pay what you want”, or even Google Drive. Really you can release it however you want, just got to tell where to find it and how to use it. If it’s currently a text file in Blender, save it as a python file (just set the file suffix to .py) making sure to save it on your system somewhere outside of Blender then upload.

I would suggest including documentation with pictures, and demonstrating some examples.

2 Likes
1 Like

You forgot a step ^. It’s no longer “an idea for a topology addon”, it’s now a released topology addon. You don’t have to post it there if you don’t want to of course, but off-topic chat isn’t where BA users go to find addons, so if the goal is to get eyes on it I would make a proper announcement in the dedicated section for promoting released addons.

1 Like