Exemplo n.º 1
0
def docyl(S, R, Pts):
    coords = []
    p0 = Vector(Pts[0][0], Pts[0][1], Pts[0][2])
    p1 = Vector(Pts[1][0], Pts[1][1], Pts[1][2])
    t = Vector(Pts[1][0] - Pts[0][0], Pts[1][1] - Pts[0][1],
               Pts[1][2] - Pts[0][2])
    t.normalize()
    if t * Vector(1, 0, 0) < 0.8:
        n = Vector(1, 0, 0) - (Vector(1, 0, 0) * t) * t
    else:
        n = Vector(0, 1, 0) - (Vector(0, 1, 0) * t) * t
    n.normalize()

    b = Vector(t[1] * n[2] - t[2] * n[1], t[2] * n[0] - t[0] * n[2],
               t[0] * n[1] - t[1] * n[0])
    b.normalize()

    for i in xrange(S):
        angle = float(i) * 2. * pi / float(S)
        vec = cos(angle) * R * n + sin(angle) * R * b

        c0 = p0 + vec
        c1 = p1 + vec
        coords += [[c0[0], c0[1], c0[2]]]
        coords += [[c1[0], c1[1], c1[2]]]

    faces = []
    for i in xrange(S):
        faces += [[
            2 * i, (2 * i + 2) % (2 * S), (2 * i + 3) % (2 * S),
            (2 * i + 1) % (2 * S)
        ]]

    # midpoints
    coords += [[p0[0], p0[1], p0[2]]]
    coords += [[p1[0], p1[1], p1[2]]]

    cid0 = len(coords) - 2
    cid1 = len(coords) - 1
    for i in xrange(S):
        idx = []
        idx2 = []
        idx.append(2 * i + 0)
        idx.append(cid0)
        idx.append(2 * ((i + 1) % S))
        idx2.append(2 * i + 1)
        idx2.append(2 * ((i + 1) % S) + 1)
        idx2.append(cid1)
        faces += [idx]
        faces += [idx2]
    print faces

    me = Mesh.New('Strut')
    me.verts.extend(coords)
    me.faces.extend(faces)

    # Add new object to scene and return it
    return Scene.GetCurrent().objects.new(me)
Exemplo n.º 2
0
def makeVNormal(me, vert, origEdgeLen):
	vNormal = Vector([0,0,0])
	eIdx = 0
	while eIdx < origEdgeLen:
		
		e = me.edges[eIdx]
		
		if e.v1 == vert or e.v2 == vert:
			pass
		else:
			eIdx+=1
			continue
			
		v1 = -1
		if vert == e.v1: v1 = False
		if vert == e.v2: v1 = 1
		if v1 == -1:
			eIdx+=1
			continue
			
		# one of the verts in the face is the same as the vert that we are finding teyh normal for.
		# Make a vector from the edge v1 and v2 are the indivies for this edge
		v2 = (not v1)
		
		ev = [e.v1, e.v2]
		
		newVec = Vector([\
			ev[v1].co[0]-ev[v2].co[0],\
			ev[v1].co[1]-ev[v2].co[1],\
			ev[v1].co[2]-ev[v2].co[2]] )
		
		newVec.normalize()
		vNormal = vNormal + newVec
		eIdx+=1
		
	vNormal.normalize()
	return vNormal
Exemplo n.º 3
0
def docyl(S,R,Pts):
  coords = []
  N = len(Pts)
  for i in xrange(N):

    prev = Vector(Pts[(i-1)%N][0],Pts[(i-1)%N][1],Pts[(i-1)%N][2])
    cur  = Vector(Pts[i][0],Pts[i][1],Pts[i][2])
    next = Vector(Pts[(i+1)%N][0],Pts[(i+1)%N][1],Pts[(i+1)%N][2])

    t = next - prev
    t.normalize()

    if t*Vector(1,0,0) < 0.8:
      n = Vector(1,0,0)-(Vector(1,0,0)*t)*t
    else:
      n = Vector(0,1,0)-(Vector(0,1,0)*t)*t
    n.normalize()

    b = Vector(t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0])
    b.normalize()

    for i in xrange(S):
      angle = float(i)*2.*pi/float(S)
      vec = cos(angle)*R*n + sin(angle)*R*b

      c0 = cur + vec
      coords += [[ c0[0],c0[1],c0[2] ]]

  faces = []
  for j in xrange(N):
    for i in xrange(S):
      faces += [[ S*j + i, S*j + (i + 1)%S, S*((j+1)%N) + (i+1)%S, S*((j+1)%N) + i ]]

  me = Mesh.New('Billiard')
  me.verts.extend(coords)
  me.faces.extend(faces)
  Scene.GetCurrent().objects.new(me)
Exemplo n.º 4
0
def docyl(S, R, P0, P1):
    coords = []
    p0 = Vector(P0[0], P0[1], P0[2])
    p1 = Vector(P1[0], P1[1], P1[2])
    t = Vector(P1[0] - P0[0], P1[1] - P0[1], P1[2] - P0[2])
    t.normalize()
    if t * Vector(1, 0, 0) < 0.8:
        n = Vector(1, 0, 0) - (Vector(1, 0, 0) * t) * t
    else:
        n = Vector(0, 1, 0) - (Vector(0, 1, 0) * t) * t
    n.normalize()

    b = Vector(t[1] * n[2] - t[2] * n[1], t[2] * n[0] - t[0] * n[2],
               t[0] * n[1] - t[1] * n[0])
    b.normalize()

    for i in xrange(S):
        angle = float(i) * 2. * pi / float(S)
        vec = cos(angle) * R * n + sin(angle) * R * b

        c0 = p0 + vec
        c1 = p1 + vec
        coords += [[c0[0], c0[1], c0[2]]]
        coords += [[c1[0], c1[1], c1[2]]]

    faces = []
    for i in xrange(S):
        faces += [[
            2 * i, (2 * i + 1) % (2 * S), (2 * i + 3) % (2 * S),
            (2 * i + 2) % (2 * S)
        ]]

    me = Mesh.New('Strut')
    me.verts.extend(coords)
    me.faces.extend(faces)
    return Scene.GetCurrent().objects.new(me)
Exemplo n.º 5
0
class edgeLoop(object):
    __slots__ = 'centre', 'edges', 'normal', 'closed', 'backup_edges'

    def __init__(self, loop, me, closed):  # Vert loop
        # Use next and prev, nextDist, prevDist

        # Get Loops centre.
        fac = len(loop)
        verts = me.verts
        self.centre = reduce(lambda a, b: a + verts[b].co / fac, loop,
                             Vector())

        # Convert Vert loop to Edges.
        self.edges = [
            edge(verts[loop[vIdx - 1]], verts[loop[vIdx]])
            for vIdx in xrange(len(loop))
        ]

        if not closed:
            self.edges[0].fake = True  # fake edge option

        self.closed = closed

        # Assign linked list
        for eIdx in xrange(len(self.edges) - 1):
            self.edges[eIdx].next = self.edges[eIdx + 1]
            self.edges[eIdx].prev = self.edges[eIdx - 1]
        # Now last
        self.edges[-1].next = self.edges[0]
        self.edges[-1].prev = self.edges[-2]

        # GENERATE AN AVERAGE NORMAL FOR THE WHOLE LOOP.
        self.normal = Vector()
        for e in self.edges:
            n = (self.centre - e.co1).cross(self.centre - e.co2)
            # Do we realy need tot normalize?
            n.normalize()
            self.normal += n

            # Generate the angle
            va = e.cent - e.prev.cent
            vb = e.next.cent - e.cent

            e.angle = AngleBetweenVecs(va, vb)

        # Blur the angles
        #for e in self.edges:
        #	e.angle= (e.angle+e.next.angle)/2

        # Blur the angles
        #for e in self.edges:
        #	e.angle= (e.angle+e.prev.angle)/2

        self.normal.normalize()

        # Generate a normal for each edge.
        for e in self.edges:

            n1 = e.co1
            n2 = e.co2
            n3 = e.prev.co1

            a = n1 - n2
            b = n1 - n3
            normal1 = a.cross(b)
            normal1.normalize()

            n1 = e.co2
            n3 = e.next.co2
            n2 = e.co1

            a = n1 - n2
            b = n1 - n3

            normal2 = a.cross(b)
            normal2.normalize()

            # Reuse normal1 var
            normal1 += normal1 + normal2
            normal1.normalize()

            e.normal = normal1
            #print e.normal

    def backup(self):
        # Keep a backup of the edges
        self.backup_edges = self.edges[:]

    def restore(self):
        self.edges = self.backup_edges[:]
        for e in self.edges:
            e.removed = 0

    def reverse(self):
        self.edges.reverse()
        self.normal.negate()

        for e in self.edges:
            e.normal.negate()
            e.v1, e.v2 = e.v2, e.v1
            e.co1, e.co2 = e.co2, e.co1
            e.next, e.prev = e.prev, e.next

    def removeSmallest(self, cullNum, otherLoopLen):
        '''
		Removes N Smallest edges and backs up the loop,
		this is so we can loop between 2 loops as if they are the same length,
		backing up and restoring incase the loop needs to be skinned with another loop of a different length.
		'''
        global CULL_METHOD
        if CULL_METHOD == 1:  # Shortest edge
            eloopCopy = self.edges[:]

            # Length sort, smallest first
            try:
                eloopCopy.sort(key=lambda e1: e1.length)
            except:
                eloopCopy.sort(lambda e1, e2: cmp(e1.length, e2.length))

            # Dont use atm
            #eloopCopy.sort(lambda e1, e2: cmp(e1.angle*e1.length, e2.angle*e2.length)) # Length sort, smallest first
            #eloopCopy.sort(lambda e1, e2: cmp(e1.angle, e2.angle)) # Length sort, smallest first

            remNum = 0
            for i, e in enumerate(eloopCopy):
                if not e.fake:
                    e.removed = 1
                    self.edges.remove(
                        e)  # Remove from own list, still in linked list.
                    remNum += 1

                    if not remNum < cullNum:
                        break

        else:  # CULL METHOD is even

            culled = 0

            step = int(otherLoopLen / float(cullNum)) * 2

            currentEdge = self.edges[0]
            while culled < cullNum:

                # Get the shortest face in the next STEP
                step_count = 0
                bestAng = 360.0
                smallestEdge = None
                while step_count <= step or smallestEdge == None:
                    step_count += 1
                    if not currentEdge.removed:  # 0 or -1 will not be accepted
                        if currentEdge.angle < bestAng and not currentEdge.fake:
                            smallestEdge = currentEdge
                            bestAng = currentEdge.angle

                    currentEdge = currentEdge.next

                # In that stepping length we have the smallest edge.remove it
                smallestEdge.removed = 1
                self.edges.remove(smallestEdge)

                # Start scanning from the edge we found? - result is over fanning- no good.
                #currentEdge= smallestEdge.next

                culled += 1
Exemplo n.º 6
0
def wireMesh(me):
	globalSize = Draw.PupFloatInput('wire width', 0.1, -10.0, 10.0, 0.01, 4)
	if globalSize == None:
		return
	globalHalfsz =globalSize/2
	
	ratio = Draw.PupMenu('Edge Width%t|Fixed Width|Scale with edge len')
	
	normalMethod = Draw.PupMenu('Use Normal%t|Vertex Normal|Edge Normal')
	
	if ratio == -1 or normalMethod == -1:
		return
	
	t = sys.time()
	origEdgeLen = len(me.edges)
	origFaceLen = len(me.faces)
	eIdx = 0
	while eIdx < origEdgeLen:
		
		e = me.edges[eIdx]
		if not e.flag & NMesh.EdgeFlags['SELECT']:
			eIdx+=1
			continue
		
		
		if ratio == 1:
			size = globalSize
			halfsz = globalHalfsz
		else:
			size = globalSize * measure(e.v1, e.v2)
			halfsz = size/2
		
		
		if normalMethod == 1: # vertex normal
			v1nor = makeVNormal(me, e.v1, origEdgeLen)
			v2nor = makeVNormal(me, e.v2, origEdgeLen)
			
			newPt1 = Vector( e.v1.co[0] + (v1nor[0]*size), e.v1.co[1] + (v1nor[1]*size), e.v1.co[2] + (v1nor[2]*size) )
			newPt2 = Vector( e.v2.co[0] + (v2nor[0]*size), e.v2.co[1] + (v2nor[1]*size), e.v2.co[2] + (v2nor[2]*size) )
			
		if normalMethod == 2: # Face Normal
			edgeNormal = Vector([e.v1.no[0] + e.v2.no[0], e.v1.no[1]+e.v2.no[1], e.v1.no[2]+e.v2.no[2] ])
			edgeNormal.normalize()
			
			newPt1 = Vector( e.v1.co[0] + (edgeNormal[0]*size), e.v1.co[1] + (edgeNormal[1]*size), e.v1.co[2] + (edgeNormal[2]*size) )
			newPt2 = Vector( e.v2.co[0] + (edgeNormal[0]*size), e.v2.co[1] + (edgeNormal[1]*size), e.v2.co[2] + (edgeNormal[2]*size) )			
		
		#		#~ newPt1************newPt2
		#		#~ *                      *
		#		#~ *                      *
		#		#~ *                      *
		#	#~ ****vert1**************Vert2**** * * * *
		norA = TriangleNormal(e.v1.co, e.v2.co, newPt1)
		norB = TriangleNormal(e.v2.co, newPt2, newPt1)
		nor1 = norA + norB
		nor1.normalize()
		
		# make face A
		me.verts.append(NMesh.Vert(e.v1.co[0] + (nor1[0]*halfsz), e.v1.co[1] + (nor1[1]*halfsz), e.v1.co[2] + (nor1[2]*halfsz)) )
		me.verts.append(NMesh.Vert(e.v2.co[0] + (nor1[0]*halfsz), e.v2.co[1] + (nor1[1]*halfsz), e.v2.co[2] + (nor1[2]*halfsz)) )
		
		me.verts.append(NMesh.Vert(newPt2[0] + (nor1[0]*halfsz), newPt2[1] + (nor1[1]*halfsz), newPt2[2] + (nor1[2]*halfsz)) )
		me.verts.append(NMesh.Vert(newPt1[0] + (nor1[0]*halfsz), newPt1[1] + (nor1[1]*halfsz), newPt1[2] + (nor1[2]*halfsz)) )
		
		fA = NMesh.Face(me.verts[-4:])		
		
		# make face B
		me.verts.append(NMesh.Vert(e.v1.co[0] + (nor1[0]*-halfsz), e.v1.co[1] + (nor1[1]*-halfsz), e.v1.co[2] + (nor1[2]*-halfsz)) )
		me.verts.append(NMesh.Vert(e.v2.co[0] + (nor1[0]*-halfsz), e.v2.co[1] + (nor1[1]*-halfsz), e.v2.co[2] + (nor1[2]*-halfsz)) )
		
		me.verts.append(NMesh.Vert(newPt2[0] + (nor1[0]*-halfsz), newPt2[1] + (nor1[1]*-halfsz), newPt2[2] + (nor1[2]*-halfsz)) )
		me.verts.append(NMesh.Vert(newPt1[0] + (nor1[0]*-halfsz), newPt1[1] + (nor1[1]*-halfsz), newPt1[2] + (nor1[2]*-halfsz)) )
		
		fB = NMesh.Face([me.verts[-1], me.verts[-2], me.verts[-3], me.verts[-4]])
		
		# make face C- top
		fC = NMesh.Face([fB.v[1], fB.v[0], fA.v[3], fA.v[2]])
		
		# make face D- bottom
		fD = NMesh.Face([fA.v[1], fA.v[0], fB.v[3], fB.v[2]])
		
		
		# a negative number is used for an inset wire- this flips the normals the wrong way- For easyness this is a simple way to fix the problem.
		if globalSize < 0:
			fA.v.reverse()
			fB.v.reverse()
			fC.v.reverse()
			fD.v.reverse()
		
		me.faces.extend([fA, fB, fC, fD])
		eIdx+=1
	print 'Wire Time: %.6f' % (sys.time()-t)
def main():
    global USER_FILL_HOLES
    global USER_FILL_HOLES_QUALITY
    global USER_STRETCH_ASPECT
    global USER_ISLAND_MARGIN

    objects = bpy.data.scenes.active.objects

    # we can will tag them later.
    obList = [ob for ob in objects.context if ob.type == 'Mesh']

    # Face select object may not be selected.
    ob = objects.active
    if ob and ob.sel == 0 and ob.type == 'Mesh':
        # Add to the list
        obList = [ob]
    del objects

    if not obList:
        Draw.PupMenu('error, no selected mesh objects')
        return

    # Create the variables.
    USER_PROJECTION_LIMIT = Draw.Create(66)
    USER_ONLY_SELECTED_FACES = Draw.Create(1)
    USER_SHARE_SPACE = Draw.Create(1)  # Only for hole filling.
    USER_STRETCH_ASPECT = Draw.Create(1)  # Only for hole filling.
    USER_ISLAND_MARGIN = Draw.Create(0.0)  # Only for hole filling.
    USER_FILL_HOLES = Draw.Create(0)
    USER_FILL_HOLES_QUALITY = Draw.Create(50)  # Only for hole filling.
    USER_VIEW_INIT = Draw.Create(0)  # Only for hole filling.
    USER_AREA_WEIGHT = Draw.Create(1)  # Only for hole filling.


    pup_block = [\
    'Projection',\
    ('Angle Limit:', USER_PROJECTION_LIMIT, 1, 89, 'lower for more projection groups, higher for less distortion.'),\
    ('Selected Faces Only', USER_ONLY_SELECTED_FACES, 'Use only selected faces from all selected meshes.'),\
    ('Init from view', USER_VIEW_INIT, 'The first projection will be from the view vector.'),\
    ('Area Weight', USER_AREA_WEIGHT, 'Weight projections vector by face area.'),\
    '',\
    '',\
    '',\
    'UV Layout',\
    ('Share Tex Space', USER_SHARE_SPACE, 'Objects Share texture space, map all objects into 1 uvmap.'),\
    ('Stretch to bounds', USER_STRETCH_ASPECT, 'Stretch the final output to texture bounds.'),\
    ('Island Margin:', USER_ISLAND_MARGIN, 0.0, 0.5, 'Margin to reduce bleed from adjacent islands.'),\
    'Fill in empty areas',\
    ('Fill Holes', USER_FILL_HOLES, 'Fill in empty areas reduced texture waistage (slow).'),\
    ('Fill Quality:', USER_FILL_HOLES_QUALITY, 1, 100, 'Depends on fill holes, how tightly to fill UV holes, (higher is slower)'),\
    ]

    # Reuse variable
    if len(obList) == 1:
        ob = "Unwrap %i Selected Mesh"
    else:
        ob = "Unwrap %i Selected Meshes"

    # HACK, loop until mouse is lifted.
    '''
	while Window.GetMouseButtons() != 0:
		sys.sleep(10)
	'''

    if not Draw.PupBlock(ob % len(obList), pup_block):
        return
    del ob

    # Convert from being button types
    USER_PROJECTION_LIMIT = USER_PROJECTION_LIMIT.val
    USER_ONLY_SELECTED_FACES = USER_ONLY_SELECTED_FACES.val
    USER_SHARE_SPACE = USER_SHARE_SPACE.val
    USER_STRETCH_ASPECT = USER_STRETCH_ASPECT.val
    USER_ISLAND_MARGIN = USER_ISLAND_MARGIN.val
    USER_FILL_HOLES = USER_FILL_HOLES.val
    USER_FILL_HOLES_QUALITY = USER_FILL_HOLES_QUALITY.val
    USER_VIEW_INIT = USER_VIEW_INIT.val
    USER_AREA_WEIGHT = USER_AREA_WEIGHT.val

    USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD)
    USER_PROJECTION_LIMIT_HALF_CONVERTED = cos(
        (USER_PROJECTION_LIMIT / 2) * DEG_TO_RAD)

    # Toggle Edit mode
    is_editmode = Window.EditMode()
    if is_editmode:
        Window.EditMode(0)
    # Assume face select mode! an annoying hack to toggle face select mode because Mesh dosent like faceSelectMode.

    if USER_SHARE_SPACE:
        # Sort by data name so we get consistant results
        try:
            obList.sort(key=lambda ob: ob.getData(name_only=1))
        except:
            obList.sort(lambda ob1, ob2: cmp(ob1.getData(name_only=1),
                                             ob2.getData(name_only=1)))

        collected_islandList = []

    Window.WaitCursor(1)

    time1 = sys.time()

    # Tag as False se we dont operate on teh same mesh twice.
    bpy.data.meshes.tag = False

    for ob in obList:
        me = ob.getData(mesh=1)

        if me.tag or me.lib:
            continue

        # Tag as used
        me.tag = True

        if not me.faceUV:  # Mesh has no UV Coords, dont bother.
            me.faceUV = True

        if USER_ONLY_SELECTED_FACES:
            meshFaces = [thickface(f) for f in me.faces if f.sel]
        else:
            meshFaces = map(thickface, me.faces)

        if not meshFaces:
            continue

        Window.DrawProgressBar(
            0.1, 'SmartProj UV Unwrapper, mapping "%s", %i faces.' %
            (me.name, len(meshFaces)))

        # =======
        # Generate a projection list from face normals, this is ment to be smart :)

        # make a list of face props that are in sync with meshFaces
        # Make a Face List that is sorted by area.
        # meshFaces = []

        # meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first.
        try:
            meshFaces.sort(key=lambda a: -a.area)
        except:
            meshFaces.sort(lambda a, b: cmp(b.area, a.area))

        # remove all zero area faces
        while meshFaces and meshFaces[-1].area <= SMALL_NUM:
            # Set their UV's to 0,0
            for uv in meshFaces[-1].uv:
                uv.zero()
            meshFaces.pop()

        # Smallest first is slightly more efficient, but if the user cancels early then its better we work on the larger data.

        # Generate Projection Vecs
        # 0d is   1.0
        # 180 IS -0.59846

        # Initialize projectVecs
        if USER_VIEW_INIT:
            # Generate Projection
            projectVecs = [
                Vector(Window.GetViewVector()) *
                ob.matrixWorld.copy().invert().rotationPart()
            ]  # We add to this allong the way
        else:
            projectVecs = []

        newProjectVec = meshFaces[0].no
        newProjectMeshFaces = []  # Popping stuffs it up.

        # Predent that the most unique angke is ages away to start the loop off
        mostUniqueAngle = -1.0

        # This is popped
        tempMeshFaces = meshFaces[:]

        # This while only gathers projection vecs, faces are assigned later on.
        while 1:
            # If theres none there then start with the largest face

            # add all the faces that are close.
            for fIdx in xrange(len(tempMeshFaces) - 1, -1, -1):
                # Use half the angle limit so we dont overweight faces towards this
                # normal and hog all the faces.
                if newProjectVec.dot(tempMeshFaces[fIdx].no
                                     ) > USER_PROJECTION_LIMIT_HALF_CONVERTED:
                    newProjectMeshFaces.append(tempMeshFaces.pop(fIdx))

            # Add the average of all these faces normals as a projectionVec
            averageVec = Vector(0, 0, 0)
            if USER_AREA_WEIGHT:
                for fprop in newProjectMeshFaces:
                    averageVec += (fprop.no * fprop.area)
            else:
                for fprop in newProjectMeshFaces:
                    averageVec += fprop.no

            if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0:  # Avoid NAN
                projectVecs.append(averageVec.normalize())

            # Get the next vec!
            # Pick the face thats most different to all existing angles :)
            mostUniqueAngle = 1.0  # 1.0 is 0d. no difference.
            mostUniqueIndex = 0  # dummy

            for fIdx in xrange(len(tempMeshFaces) - 1, -1, -1):
                angleDifference = -1.0  # 180d difference.

                # Get the closest vec angle we are to.
                for p in projectVecs:
                    temp_angle_diff = p.dot(tempMeshFaces[fIdx].no)

                    if angleDifference < temp_angle_diff:
                        angleDifference = temp_angle_diff

                if angleDifference < mostUniqueAngle:
                    # We have a new most different angle
                    mostUniqueIndex = fIdx
                    mostUniqueAngle = angleDifference

            if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED:
                #print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces)
                # Now weight the vector to all its faces, will give a more direct projection
                # if the face its self was not representive of the normal from surrounding faces.

                newProjectVec = tempMeshFaces[mostUniqueIndex].no
                newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)]

            else:
                if len(projectVecs) >= 1:  # Must have at least 2 projections
                    break

        # If there are only zero area faces then its possible
        # there are no projectionVecs
        if not len(projectVecs):
            Draw.PupMenu(
                'error, no projection vecs where generated, 0 area faces can cause this.'
            )
            return

        faceProjectionGroupList = [[] for i in xrange(len(projectVecs))]

        # MAP and Arrange # We know there are 3 or 4 faces here

        for fIdx in xrange(len(meshFaces) - 1, -1, -1):
            fvec = meshFaces[fIdx].no
            i = len(projectVecs)

            # Initialize first
            bestAng = fvec.dot(projectVecs[0])
            bestAngIdx = 0

            # Cycle through the remaining, first alredy done
            while i - 1:
                i -= 1

                newAng = fvec.dot(projectVecs[i])
                if newAng > bestAng:  # Reverse logic for dotvecs
                    bestAng = newAng
                    bestAngIdx = i

            # Store the area for later use.
            faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx])

        # Cull faceProjectionGroupList,

        # Now faceProjectionGroupList is full of faces that face match the project Vecs list
        for i in xrange(len(projectVecs)):
            # Account for projectVecs having no faces.
            if not faceProjectionGroupList[i]:
                continue

            # Make a projection matrix from a unit length vector.
            MatProj = VectoMat(projectVecs[i])

            # Get the faces UV's from the projected vertex.
            for f in faceProjectionGroupList[i]:
                f_uv = f.uv
                for j, v in enumerate(f.v):
                    f_uv[j][:] = (MatProj * v.co)[:2]

        if USER_SHARE_SPACE:
            # Should we collect and pack later?
            islandList = getUvIslands(faceProjectionGroupList, me)
            collected_islandList.extend(islandList)

        else:
            # Should we pack the islands for this 1 object?
            islandList = getUvIslands(faceProjectionGroupList, me)
            packIslands(islandList)

        # update the mesh here if we need to.

    # We want to pack all in 1 go, so pack now
    if USER_SHARE_SPACE:
        Window.DrawProgressBar(0.9, "Box Packing for all objects...")
        packIslands(collected_islandList)

    print "Smart Projection time: %.2f" % (sys.time() - time1)
    # Window.DrawProgressBar(0.9, "Smart Projections done, time: %.2f sec." % (sys.time() - time1))

    if is_editmode:
        Window.EditMode(1)

    Window.DrawProgressBar(1.0, "")
    Window.WaitCursor(0)
    Window.RedrawAll()
Exemplo n.º 8
0
def uvcalc_main(obList):
    global USER_FILL_HOLES
    global USER_FILL_HOLES_QUALITY
    global USER_STRETCH_ASPECT
    global USER_ISLAND_MARGIN

    # objects= bpy.data.scenes.active.objects

    # we can will tag them later.
    # obList =  [ob for ob in objects.context if ob.type == 'Mesh']

    # Face select object may not be selected.
    # ob = objects.active
    # if ob and ob.sel == 0 and ob.type == 'Mesh':
    # 	# Add to the list
    # 	obList =[ob]
    # del objects

    if not obList:
        Draw.PupMenu("error, no selected mesh objects")
        return

        # Create the variables.
    USER_PROJECTION_LIMIT = Draw.Create(66)
    USER_ONLY_SELECTED_FACES = Draw.Create(1)
    USER_SHARE_SPACE = Draw.Create(1)  # Only for hole filling.
    USER_STRETCH_ASPECT = Draw.Create(1)  # Only for hole filling.
    USER_ISLAND_MARGIN = Draw.Create(0.0)  # Only for hole filling.
    USER_FILL_HOLES = Draw.Create(0)
    USER_FILL_HOLES_QUALITY = Draw.Create(50)  # Only for hole filling.
    USER_VIEW_INIT = Draw.Create(0)  # Only for hole filling.
    USER_AREA_WEIGHT = Draw.Create(1)  # Only for hole filling.

    pup_block = [
        "Projection",
        ("Angle Limit:", USER_PROJECTION_LIMIT, 1, 89, "lower for more projection groups, higher for less distortion."),
        ("Selected Faces Only", USER_ONLY_SELECTED_FACES, "Use only selected faces from all selected meshes."),
        ("Init from view", USER_VIEW_INIT, "The first projection will be from the view vector."),
        ("Area Weight", USER_AREA_WEIGHT, "Weight projections vector by face area."),
        "",
        "",
        "",
        "UV Layout",
        ("Share Tex Space", USER_SHARE_SPACE, "Objects Share texture space, map all objects into 1 uvmap."),
        ("Stretch to bounds", USER_STRETCH_ASPECT, "Stretch the final output to texture bounds."),
        ("Island Margin:", USER_ISLAND_MARGIN, 0.0, 0.5, "Margin to reduce bleed from adjacent islands."),
        "Fill in empty areas",
        ("Fill Holes", USER_FILL_HOLES, "Fill in empty areas reduced texture waistage (slow)."),
        (
            "Fill Quality:",
            USER_FILL_HOLES_QUALITY,
            1,
            100,
            "Depends on fill holes, how tightly to fill UV holes, (higher is slower)",
        ),
    ]

    # Reuse variable
    if len(obList) == 1:
        ob = "Unwrap %i Selected Mesh"
    else:
        ob = "Unwrap %i Selected Meshes"

        # if not Draw.PupBlock(ob % len(obList), pup_block):
        # 	return
        # del ob

        # Convert from being button types
    USER_PROJECTION_LIMIT = USER_PROJECTION_LIMIT.val
    USER_ONLY_SELECTED_FACES = USER_ONLY_SELECTED_FACES.val
    USER_SHARE_SPACE = USER_SHARE_SPACE.val
    USER_STRETCH_ASPECT = USER_STRETCH_ASPECT.val
    USER_ISLAND_MARGIN = USER_ISLAND_MARGIN.val
    USER_FILL_HOLES = USER_FILL_HOLES.val
    USER_FILL_HOLES_QUALITY = USER_FILL_HOLES_QUALITY.val
    USER_VIEW_INIT = USER_VIEW_INIT.val
    USER_AREA_WEIGHT = USER_AREA_WEIGHT.val

    USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD)
    USER_PROJECTION_LIMIT_HALF_CONVERTED = cos((USER_PROJECTION_LIMIT / 2) * DEG_TO_RAD)

    # Toggle Edit mode
    # is_editmode = Window.EditMode()
    # if is_editmode:
    # 	Window.EditMode(0)
    # Assume face select mode! an annoying hack to toggle face select mode because Mesh dosent like faceSelectMode.

    if USER_SHARE_SPACE:
        # Sort by data name so we get consistant results
        try:
            obList.sort(key=lambda ob: ob.getData(name_only=1))
        except:
            obList.sort(lambda ob1, ob2: cmp(ob1.getData(name_only=1), ob2.getData(name_only=1)))

        collected_islandList = []

        # Window.WaitCursor(1)

    time1 = sys.time()

    # Tag as False se we dont operate on teh same mesh twice.
    bpy.data.meshes.tag = False

    for ob in obList:
        me = ob.getData(mesh=1)

        if me.tag or me.lib:
            continue

            # Tag as used
        me.tag = True

        if not me.faceUV:  # Mesh has no UV Coords, dont bother.
            me.faceUV = True

        if USER_ONLY_SELECTED_FACES:
            meshFaces = [thickface(f) for f in me.faces if f.sel]
        else:
            meshFaces = map(thickface, me.faces)

        if not meshFaces:
            continue

            # Window.DrawProgressBar(0.1, 'SmartProj UV Unwrapper, mapping "%s", %i faces.' % (me.name, len(meshFaces)))

            # =======
            # Generate a projection list from face normals, this is ment to be smart :)

            # make a list of face props that are in sync with meshFaces
            # Make a Face List that is sorted by area.
            # meshFaces = []

            # meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first.
        try:
            meshFaces.sort(key=lambda a: -a.area)
        except:
            meshFaces.sort(lambda a, b: cmp(b.area, a.area))

        # remove all zero area faces
        while meshFaces and meshFaces[-1].area <= SMALL_NUM:
            # Set their UV's to 0,0
            for uv in meshFaces[-1].uv:
                uv.zero()
            meshFaces.pop()

            # Smallest first is slightly more efficient, but if the user cancels early then its better we work on the larger data.

            # Generate Projection Vecs
            # 0d is   1.0
            # 180 IS -0.59846

            # Initialize projectVecs
        if USER_VIEW_INIT:
            # Generate Projection
            projectVecs = [
                Vector(Window.GetViewVector()) * ob.matrixWorld.copy().invert().rotationPart()
            ]  # We add to this allong the way
        else:
            projectVecs = []

        newProjectVec = meshFaces[0].no
        newProjectMeshFaces = []  # Popping stuffs it up.

        # Predent that the most unique angke is ages away to start the loop off
        mostUniqueAngle = -1.0

        # This is popped
        tempMeshFaces = meshFaces[:]

        # This while only gathers projection vecs, faces are assigned later on.
        while 1:
            # If theres none there then start with the largest face

            # add all the faces that are close.
            for fIdx in xrange(len(tempMeshFaces) - 1, -1, -1):
                # Use half the angle limit so we dont overweight faces towards this
                # normal and hog all the faces.
                if newProjectVec.dot(tempMeshFaces[fIdx].no) > USER_PROJECTION_LIMIT_HALF_CONVERTED:
                    newProjectMeshFaces.append(tempMeshFaces.pop(fIdx))

                    # Add the average of all these faces normals as a projectionVec
            averageVec = Vector(0, 0, 0)
            if USER_AREA_WEIGHT:
                for fprop in newProjectMeshFaces:
                    averageVec += fprop.no * fprop.area
            else:
                for fprop in newProjectMeshFaces:
                    averageVec += fprop.no

            if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0:  # Avoid NAN
                projectVecs.append(averageVec.normalize())

                # Get the next vec!
                # Pick the face thats most different to all existing angles :)
            mostUniqueAngle = 1.0  # 1.0 is 0d. no difference.
            mostUniqueIndex = 0  # dummy

            for fIdx in xrange(len(tempMeshFaces) - 1, -1, -1):
                angleDifference = -1.0  # 180d difference.

                # Get the closest vec angle we are to.
                for p in projectVecs:
                    temp_angle_diff = p.dot(tempMeshFaces[fIdx].no)

                    if angleDifference < temp_angle_diff:
                        angleDifference = temp_angle_diff

                if angleDifference < mostUniqueAngle:
                    # We have a new most different angle
                    mostUniqueIndex = fIdx
                    mostUniqueAngle = angleDifference

            if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED:
                # print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces)
                # Now weight the vector to all its faces, will give a more direct projection
                # if the face its self was not representive of the normal from surrounding faces.

                newProjectVec = tempMeshFaces[mostUniqueIndex].no
                newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)]

            else:
                if len(projectVecs) >= 1:  # Must have at least 2 projections
                    break

                    # If there are only zero area faces then its possible
                    # there are no projectionVecs
        if not len(projectVecs):
            Draw.PupMenu("error, no projection vecs where generated, 0 area faces can cause this.")
            return

        faceProjectionGroupList = [[] for i in xrange(len(projectVecs))]

        # MAP and Arrange # We know there are 3 or 4 faces here

        for fIdx in xrange(len(meshFaces) - 1, -1, -1):
            fvec = meshFaces[fIdx].no
            i = len(projectVecs)

            # Initialize first
            bestAng = fvec.dot(projectVecs[0])
            bestAngIdx = 0

            # Cycle through the remaining, first alredy done
            while i - 1:
                i -= 1

                newAng = fvec.dot(projectVecs[i])
                if newAng > bestAng:  # Reverse logic for dotvecs
                    bestAng = newAng
                    bestAngIdx = i

                    # Store the area for later use.
            faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx])

            # Cull faceProjectionGroupList,

            # Now faceProjectionGroupList is full of faces that face match the project Vecs list
        for i in xrange(len(projectVecs)):
            # Account for projectVecs having no faces.
            if not faceProjectionGroupList[i]:
                continue

                # Make a projection matrix from a unit length vector.
            MatProj = VectoMat(projectVecs[i])

            # Get the faces UV's from the projected vertex.
            for f in faceProjectionGroupList[i]:
                f_uv = f.uv
                for j, v in enumerate(f.v):
                    f_uv[j][:] = (MatProj * v.co)[:2]

        if USER_SHARE_SPACE:
            # Should we collect and pack later?
            islandList = getUvIslands(faceProjectionGroupList, me)
            collected_islandList.extend(islandList)

        else:
            # Should we pack the islands for this 1 object?
            islandList = getUvIslands(faceProjectionGroupList, me)
            packIslands(islandList)

            # update the mesh here if we need to.

            # We want to pack all in 1 go, so pack now
    if USER_SHARE_SPACE:
        # Window.DrawProgressBar(0.9, "Box Packing for all objects...")
        packIslands(collected_islandList)

    print "Smart Projection time: %.2f" % (sys.time() - time1)
Exemplo n.º 9
0
class edgeLoop(object):
	__slots__ = 'centre', 'edges', 'normal', 'closed', 'backup_edges'
	def __init__(self, loop, me, closed): # Vert loop
		# Use next and prev, nextDist, prevDist
		
		# Get Loops centre.
		fac= len(loop)
		verts = me.verts
		self.centre= reduce(lambda a,b: a+verts[b].co/fac, loop, Vector())
		
		# Convert Vert loop to Edges.
		self.edges = [edge(verts[loop[vIdx-1]], verts[loop[vIdx]]) for vIdx in xrange(len(loop))]
		
		if not closed:
			self.edges[0].fake = True # fake edge option
			
		self.closed = closed
			
		
		# Assign linked list
		for eIdx in xrange(len(self.edges)-1):
			self.edges[eIdx].next = self.edges[eIdx+1]
			self.edges[eIdx].prev = self.edges[eIdx-1]
		# Now last
		self.edges[-1].next = self.edges[0]
		self.edges[-1].prev = self.edges[-2]
		
		
		
		# GENERATE AN AVERAGE NORMAL FOR THE WHOLE LOOP.
		self.normal = Vector()
		for e in self.edges:
			n = (self.centre-e.co1).cross(self.centre-e.co2)
			# Do we realy need tot normalize?
			n.normalize()
			self.normal += n
			
			# Generate the angle
			va= e.cent - e.prev.cent
			vb= e.next.cent - e.cent
			
			e.angle= AngleBetweenVecs(va, vb)
		
		# Blur the angles
		#for e in self.edges:
		#	e.angle= (e.angle+e.next.angle)/2
		
		# Blur the angles
		#for e in self.edges:
		#	e.angle= (e.angle+e.prev.angle)/2
			
		self.normal.normalize()
		
		# Generate a normal for each edge.
		for e in self.edges:
			
			n1 = e.co1
			n2 = e.co2
			n3 = e.prev.co1
			
			a = n1-n2
			b = n1-n3
			normal1 = a.cross(b)
			normal1.normalize()
			
			n1 = e.co2
			n3 = e.next.co2
			n2 = e.co1
			
			a = n1-n2
			b = n1-n3
			
			normal2 = a.cross(b)
			normal2.normalize()
			
			# Reuse normal1 var
			normal1 += normal1 + normal2
			normal1.normalize()
			
			e.normal = normal1
			#print e.normal


		
	def backup(self):
		# Keep a backup of the edges
		self.backup_edges = self.edges[:]
			
	def restore(self):
		self.edges = self.backup_edges[:]
		for e in self.edges:
			e.removed = 0
		
	def reverse(self):
		self.edges.reverse()
		self.normal.negate()
		
		for e in self.edges:
			e.normal.negate()
			e.v1, e.v2 = e.v2, e.v1
			e.co1, e.co2 = e.co2, e.co1
			e.next, e.prev = e.prev, e.next
		
	
	def removeSmallest(self, cullNum, otherLoopLen):
		'''
		Removes N Smallest edges and backs up the loop,
		this is so we can loop between 2 loops as if they are the same length,
		backing up and restoring incase the loop needs to be skinned with another loop of a different length.
		'''
		global CULL_METHOD
		if CULL_METHOD == 1: # Shortest edge
			eloopCopy = self.edges[:]
			
			# Length sort, smallest first
			try:	eloopCopy.sort(key = lambda e1: e1.length)
			except:	eloopCopy.sort(lambda e1, e2: cmp(e1.length, e2.length ))
			
			# Dont use atm
			#eloopCopy.sort(lambda e1, e2: cmp(e1.angle*e1.length, e2.angle*e2.length)) # Length sort, smallest first
			#eloopCopy.sort(lambda e1, e2: cmp(e1.angle, e2.angle)) # Length sort, smallest first
			
			remNum = 0
			for i, e in enumerate(eloopCopy):
				if not e.fake:
					e.removed = 1
					self.edges.remove( e ) # Remove from own list, still in linked list.
					remNum += 1
				
					if not remNum < cullNum:
						break
			
		else: # CULL METHOD is even
				
			culled = 0
			
			step = int(otherLoopLen / float(cullNum)) * 2
			
			currentEdge = self.edges[0]
			while culled < cullNum:
				
				# Get the shortest face in the next STEP
				step_count= 0
				bestAng= 360.0
				smallestEdge= None
				while step_count<=step or smallestEdge==None:
					step_count+=1
					if not currentEdge.removed: # 0 or -1 will not be accepted
						if currentEdge.angle<bestAng and not currentEdge.fake:
							smallestEdge= currentEdge
							bestAng= currentEdge.angle
					
					currentEdge = currentEdge.next
				
				# In that stepping length we have the smallest edge.remove it
				smallestEdge.removed = 1
				self.edges.remove(smallestEdge)
				
				# Start scanning from the edge we found? - result is over fanning- no good.
				#currentEdge= smallestEdge.next
				
				culled+=1