Updated for new Mathutils (should be faster)
uses Mesh rather then NMesh uses expernal box packer uv coords are not stretched anynore.
This commit is contained in:
@@ -1,27 +1,26 @@
|
||||
#!BPY
|
||||
|
||||
""" Registration info for Blender menus: <- these words are ignored
|
||||
Name: 'ArchiMap UV Unwrapper'
|
||||
Blender: 237
|
||||
Name: 'ArchiMap UV Projection Unwrapper'
|
||||
Blender: 240
|
||||
Group: 'UV'
|
||||
Tooltip: 'ArchiMap UV Unwrap Mesh faces.'
|
||||
Tooltip: 'ArchiMap UV Unwrap mesh faces for all select mesh objects'
|
||||
"""
|
||||
|
||||
|
||||
__author__ = "Campbell Barton"
|
||||
__url__ = ("blender", "elysiun")
|
||||
__version__ = "1.0 6/13/05"
|
||||
__version__ = "1.1 12/18/05"
|
||||
|
||||
__bpydoc__ = """\
|
||||
This script projection unwraps the selected faces of a mesh.
|
||||
|
||||
It operates on all selected mesh objects, and can be set to unwrap
|
||||
it operates on all selected mesh objects, and can be set to unwrap
|
||||
selected faces, or all faces.
|
||||
|
||||
"""
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Archimap UV Projection Unwrapper v1.0 by Campbell Barton (AKA Ideasman)
|
||||
# Archimap UV Projection Unwrapper v1.1 by Campbell Barton (AKA Ideasman)
|
||||
# --------------------------------------------------------------------------
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
@@ -43,509 +42,23 @@ selected faces, or all faces.
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
from Blender import *
|
||||
from Blender import Mathutils
|
||||
from Blender.Mathutils import *
|
||||
from Blender.Mathutils import CrossVecs, Matrix, Vector, RotationMatrix, DotVecs, TriangleArea
|
||||
|
||||
from math import cos, acos,pi,sqrt
|
||||
from math import cos
|
||||
|
||||
|
||||
try:
|
||||
import sys as py_sys
|
||||
except:
|
||||
py_sys = None
|
||||
|
||||
|
||||
DEG_TO_RAD = pi/180.0
|
||||
DEG_TO_RAD = 0.017453292519943295 # pi/180.0
|
||||
SMALL_NUM = 0.000000001
|
||||
BIG_NUM = 1e15
|
||||
|
||||
|
||||
|
||||
try:
|
||||
dummy = Mathutils.Matrix( Mathutils.Matrix() )
|
||||
del dummy
|
||||
NEW_2_38_MATHUTILS = True
|
||||
except TypeError:
|
||||
NEW_2_38_MATHUTILS = False
|
||||
|
||||
|
||||
global USER_FILL_HOLES
|
||||
global USER_FILL_HOLES_QUALITY
|
||||
USER_FILL_HOLES = None
|
||||
USER_FILL_HOLES_QUALITY = None
|
||||
|
||||
# ================================================== USER_BOX_PACK_MOD.py
|
||||
from Blender import NMesh, Window, sys
|
||||
|
||||
# a box packing vert
|
||||
class vt:
|
||||
def __init__(self, x,y):
|
||||
self.x, self.y = x, y
|
||||
|
||||
self.free = 15
|
||||
|
||||
# Set flags so cant test bottom left of 0/0
|
||||
#~ BLF = 1; TRF = 2; TLF = 4; BRF = 8
|
||||
|
||||
#self.users = [] # A list of boxes.
|
||||
# Rather then users, store Quadrents
|
||||
self.blb = self.tlb = self.brb = self.trb = None
|
||||
|
||||
|
||||
# A hack to remember the box() that last intersectec this vert
|
||||
self.intersectCache = ([], [], [], [])
|
||||
|
||||
class vertList:
|
||||
def __init__(self, verts=[]):
|
||||
self.verts = verts
|
||||
|
||||
# Sorts closest first. - uses the box w/h as a bias,
|
||||
# this makes it so its less likely to have lots of poking out bits
|
||||
# that use too much
|
||||
# Lambada based sort
|
||||
def sortCorner(self,w,h):
|
||||
self.verts.sort(lambda A, B: cmp(max(A.x+w, A.y+h) , max(B.x+w, B.y+h))) # Reverse area sort
|
||||
|
||||
|
||||
class box:
|
||||
global packedVerts
|
||||
def __init__(self, width, height, id=None):
|
||||
global packedVerts
|
||||
|
||||
self.id= id
|
||||
|
||||
self.area = width * height # real area
|
||||
self.farea = width + height # fake area
|
||||
#self.farea = float(min(width, height)) / float(max(width, height)) # fake area
|
||||
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
# Append 4 new verts
|
||||
# (BL,TR,TL,BR) / 0,1,2,3
|
||||
self.v = [vt(0,0), vt(width,height), vt(0,height), vt(width,0)]
|
||||
|
||||
# Set the interior quadrents as used.
|
||||
self.v[0].free &= ~TRF
|
||||
self.v[1].free &= ~BLF
|
||||
self.v[2].free &= ~BRF
|
||||
self.v[3].free &= ~TLF
|
||||
|
||||
#for v in self.v:
|
||||
# v.users.append(self)
|
||||
self.v[0].trb = self
|
||||
self.v[1].blb = self
|
||||
self.v[2].brb = self
|
||||
self.v[3].tlb = self
|
||||
|
||||
|
||||
|
||||
# Updates verts 3 & 4 from 1 and 2
|
||||
# since 3 and 4 are only there foill need is resizing/ rotating of patterns on the fly while I painr new box placement
|
||||
# but may be merged later with other verts
|
||||
def updateV34(self):
|
||||
|
||||
self.v[TL].x = self.v[BL].x
|
||||
self.v[TL].y = self.v[TR].y
|
||||
|
||||
self.v[BR].x = self.v[TR].x
|
||||
self.v[BR].y = self.v[BL].y
|
||||
|
||||
|
||||
def setLeft(self, lft=None):
|
||||
self.v[TR].x = lft + self.v[TR].x - self.v[BL].x
|
||||
self.v[BL].x = lft
|
||||
# update othere verts
|
||||
self.updateV34()
|
||||
|
||||
def setRight(self, rgt=None):
|
||||
self.v[BL].x = rgt - (self.v[TR].x - self.v[BL].x)
|
||||
self.v[TR].x = rgt
|
||||
self.updateV34()
|
||||
|
||||
def setBottom(self, btm=None):
|
||||
self.v[TR].y = btm + self.v[TR].y - self.v[BL].y
|
||||
self.v[BL].y = btm
|
||||
self.updateV34()
|
||||
|
||||
def setTop(self, tp=None):
|
||||
self.v[BL].y = tp - (self.v[TR].y - self.v[BL].y)
|
||||
self.v[TR].y = tp
|
||||
self.updateV34()
|
||||
|
||||
def getLeft(self):
|
||||
return self.v[BL].x
|
||||
|
||||
def getRight(self):
|
||||
return self.v[TR].x
|
||||
|
||||
def getBottom(self):
|
||||
return self.v[BL].y
|
||||
|
||||
def getTop(self):
|
||||
return self.v[TR].y
|
||||
|
||||
# Returns none, meaning it didnt overlap any new boxes
|
||||
def overlapAll(self, boxLs, intersectCache): # Flag index lets us know which quadere
|
||||
if self.v[BL].x < 0:
|
||||
return None
|
||||
elif self.v[BL].y < 0:
|
||||
return None
|
||||
else:
|
||||
bIdx = len(intersectCache)
|
||||
while bIdx:
|
||||
bIdx-=1
|
||||
b = intersectCache[bIdx]
|
||||
if not (self.v[TR].y <= b.v[BL].y or\
|
||||
self.v[BL].y >= b.v[TR].y or\
|
||||
self.v[BL].x >= b.v[TR].x or\
|
||||
self.v[TR].x <= b.v[BL].x ):
|
||||
|
||||
return None # Intersection with existing box
|
||||
#return 0 # Must keep looking
|
||||
|
||||
|
||||
for b in boxLs.boxes:
|
||||
if not (self.v[TR].y <= b.v[BL].y or\
|
||||
self.v[BL].y >= b.v[TR].y or\
|
||||
self.v[BL].x >= b.v[TR].x or\
|
||||
self.v[TR].x <= b.v[BL].x ):
|
||||
|
||||
return b # Intersection with new box.
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
def place(self, vert, quad):
|
||||
if quad == BLF:
|
||||
self.setLeft(vert.x)
|
||||
self.setBottom(vert.y)
|
||||
|
||||
elif quad == TRF:
|
||||
self.setRight(vert.x)
|
||||
self.setBottom(vert.y)
|
||||
|
||||
elif quad == TLF:
|
||||
self.setLeft(vert.x)
|
||||
self.setTop(vert.y)
|
||||
|
||||
elif quad == BRF:
|
||||
self.setRight(vert.x)
|
||||
self.setTop(vert.y)
|
||||
|
||||
# Trys to lock a box onto another box's verts
|
||||
# cleans up double verts after
|
||||
def tryVert(self, boxes, baseVert):
|
||||
flagIndex = -1
|
||||
for freeQuad in quadFlagLs:
|
||||
flagIndex +=1
|
||||
#print 'Testing ', self.width
|
||||
if not baseVert.free & freeQuad:
|
||||
continue
|
||||
|
||||
self.place(baseVert, freeQuad)
|
||||
overlapBox = self.overlapAll(boxes, baseVert.intersectCache[flagIndex])
|
||||
if overlapBox == 0: # There is no overlap
|
||||
baseVert.free &= ~freeQuad # Removes quad
|
||||
# Appends all verts but the one that matches. this removes the need for remove doubles
|
||||
for vIdx in range(4): # (BL,TR,TL,BR): # (BL,TR,TL,BR) / 0,1,2,3
|
||||
self_v = self.v[vIdx] # shortcut
|
||||
if not (self_v.x == baseVert.x and self_v.y == baseVert.y):
|
||||
packedVerts.verts.append(self_v)
|
||||
else:
|
||||
baseVert.free &= self.v[vIdx].free # make sure the
|
||||
|
||||
# Inherit used boxes from old verts
|
||||
if self_v.blb: baseVert.blb = self_v.blb
|
||||
if self_v.brb: baseVert.brb = self_v.brb #print 'inherit2'
|
||||
if self_v.tlb: baseVert.tlb = self_v.tlb #print 'inherit3'
|
||||
if self_v.trb: baseVert.trb = self_v.trb #print 'inherit4'
|
||||
self.v[vIdx] = baseVert
|
||||
|
||||
|
||||
# ========================== WHY DOSENT THIS WORK???
|
||||
#~ if baseVert.tlb and baseVert.trb:
|
||||
#~ if self == baseVert.tlb or self == baseVert.trb:
|
||||
|
||||
#~ if baseVert.tlb.height > baseVert.trb.height:
|
||||
#~ #baseVert.trb.v[TL].free &= ~TLF & ~BLF
|
||||
#~ baseVert.trb.v[TL].free &= ~TLF
|
||||
#~ baseVert.trb.v[TL].free &= ~BLF
|
||||
|
||||
#~ elif baseVert.tlb.height < baseVert.trb.height:
|
||||
#~ #baseVert.trb.v[TL].free &= ~TLF & ~BLF
|
||||
#~ baseVert.tlb.v[TR].free &= ~TRF
|
||||
#~ baseVert.tlb.v[TR].free &= ~BRF
|
||||
#~ else: # same
|
||||
#~ baseVert.tlb.v[TR].free &= ~BLF
|
||||
#~ baseVert.trb.v[TL].free &= ~BRF
|
||||
|
||||
|
||||
#~ if baseVert.blb and baseVert.brb:
|
||||
#~ if self == baseVert.blb or self == baseVert.brb:
|
||||
|
||||
#~ if baseVert.blb.height > baseVert.brb.height:
|
||||
#~ #baseVert.trb.v[TL].free &= ~TLF & ~BLF
|
||||
#~ baseVert.brb.v[BL].free &= ~TLF
|
||||
#~ baseVert.brb.v[BL].free &= ~BLF
|
||||
|
||||
#~ elif baseVert.blb.height < baseVert.brb.height:
|
||||
#~ #baseVert.trb.v[TL].free &= ~TLF & ~BLF
|
||||
#~ baseVert.blb.v[BR].free &= ~TRF
|
||||
#~ baseVert.blb.v[BR].free &= ~BRF
|
||||
#~ else: # same
|
||||
#~ baseVert.blb.v[BR].free &= ~TLF
|
||||
#~ baseVert.brb.v[BL].free &= ~TRF
|
||||
|
||||
#~ # print 'Hay', baseVert.tlb.height, baseVert.trb.height
|
||||
|
||||
|
||||
|
||||
return 1 # Working
|
||||
|
||||
# We have a box that intersects that quadrent.
|
||||
elif overlapBox != None: # None is used for a box thats alredt in the freq list.
|
||||
|
||||
# There was an overlap, add this box to the verts list
|
||||
#quadFlagLs = (BLF,BRF,TLF,TRF)
|
||||
baseVert.intersectCache[flagIndex].append(overlapBox)
|
||||
|
||||
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class boxList:
|
||||
global packedVerts
|
||||
def __init__(self, boxes):
|
||||
self.boxes = boxes
|
||||
|
||||
# keep a running update of the width and height so we know the area
|
||||
# initialize with first box, fixes but where we whwere only packing 1 box
|
||||
self.width = 0
|
||||
self.height = 0
|
||||
if len(boxes) > 0:
|
||||
for b in boxes:
|
||||
self.width = max(self.width, b.width)
|
||||
self.height = max(self.height, b.height)
|
||||
|
||||
|
||||
|
||||
|
||||
# boxArea is the total area of all boxes in the list,
|
||||
# can be used with packArea() to determine waistage.
|
||||
self.boxArea = 0 # incremented with addBox()
|
||||
|
||||
|
||||
# Just like MyBoxLs.boxes.append(), but sets bounds
|
||||
def addBoxPack(self, box):
|
||||
|
||||
|
||||
# Resize this boxlist bounds for the current box.
|
||||
self.width = max(self.width, box.getRight())
|
||||
self.height = max(self.height, box.getTop())
|
||||
|
||||
self.boxArea += box.area
|
||||
|
||||
|
||||
|
||||
# iterate through these
|
||||
#~ quadFlagLs = (1,8,4,2)
|
||||
#~ # Flags for vert idx used quads
|
||||
#~ BLF = 1; TRF = 2; TLF = 4; BRF = 8
|
||||
#~ quadFlagLs = (BLF,BRF,TLF,TRF)
|
||||
|
||||
# Look through all the free vert quads and see if there are some we can remove
|
||||
'''
|
||||
for v in box.v:
|
||||
|
||||
# Is my bottom being used.
|
||||
|
||||
if v.free & BLF and v.free & BRF: # BLF and BRF
|
||||
for b in self.boxes:
|
||||
if b.v[TR].y == v.y:
|
||||
if b.v[TR].x > v.x:
|
||||
if b.v[BL].x < v.x:
|
||||
v.free &= ~BLF # Removes quad
|
||||
v.free &= ~BRF # Removes quad
|
||||
|
||||
# Is my left being used.
|
||||
if v.free & BLF and v.free & TLF:
|
||||
for b in self.boxes:
|
||||
if b.v[TR].x == v.x:
|
||||
if b.v[TR].y > v.y:
|
||||
if b.v[BL].y < v.y:
|
||||
v.free &= ~BLF # Removes quad
|
||||
v.free &= ~TLF # Removes quad
|
||||
|
||||
if v.free & TRF and v.free & TLF:
|
||||
# Is my top being used.
|
||||
for b in self.boxes:
|
||||
if b.v[BL].y == v.y:
|
||||
if b.v[TR].x > v.x:
|
||||
if b.v[BL].x < v.x:
|
||||
v.free &= ~TLF # Removes quad
|
||||
v.free &= ~TRF # Removes quad
|
||||
|
||||
|
||||
# Is my right being used.
|
||||
if v.free & TRF and v.free & BRF:
|
||||
for b in self.boxes:
|
||||
if b.v[BL].x == v.x:
|
||||
if b.v[TR].y > v.y:
|
||||
if b.v[BL].y < v.y:
|
||||
v.free &= ~BRF # Removes quad
|
||||
v.free &= ~TRF # Removes quad
|
||||
|
||||
'''
|
||||
self.boxes.append(box)
|
||||
|
||||
|
||||
|
||||
# Just like MyBoxLs.boxes.append(), but sets bounds
|
||||
def addBox(self, box):
|
||||
self.boxes.append(box)
|
||||
self.boxArea += box.area
|
||||
|
||||
# The area of the backing bounds.
|
||||
def packedArea(self):
|
||||
return self.width * self.height
|
||||
|
||||
# Sort boxes by area
|
||||
# TODO REPLACE WITH SORT(LAMBDA(CMP...))
|
||||
def sortArea(self):
|
||||
self.boxes.sort(lambda A, B: cmp(B.area, A.area) ) # Reverse area sort
|
||||
|
||||
# BLENDER only
|
||||
def draw(self):
|
||||
m = NMesh.GetRaw()
|
||||
|
||||
|
||||
for b in self.boxes:
|
||||
z = min(b.width, b.height ) / max(b.width, b.height )
|
||||
#z = b.farea
|
||||
#z=0
|
||||
f = NMesh.Face()
|
||||
m.verts.append(NMesh.Vert(b.getLeft(), b.getBottom(), z))
|
||||
f.v.append(m.verts[-1])
|
||||
m.verts.append(NMesh.Vert(b.getRight(), b.getBottom(), z))
|
||||
f.v.append(m.verts[-1])
|
||||
m.verts.append(NMesh.Vert(b.getRight(), b.getTop(), z))
|
||||
f.v.append(m.verts[-1])
|
||||
m.verts.append(NMesh.Vert(b.getLeft(), b.getTop(), z))
|
||||
f.v.append(m.verts[-1])
|
||||
m.faces.append(f)
|
||||
NMesh.PutRaw(m, 's')
|
||||
Window.Redraw(1)
|
||||
|
||||
def pack(self):
|
||||
global packedVerts
|
||||
packedVerts = vertList()
|
||||
|
||||
self.sortArea()
|
||||
|
||||
if len(self.boxes) == 0:
|
||||
return
|
||||
|
||||
packedboxes = boxList([self.boxes[0]])
|
||||
|
||||
# Remove verts we KNOW cant be added to
|
||||
|
||||
unpackedboxes = boxList(self.boxes[1:])
|
||||
|
||||
# STart with this box
|
||||
packedVerts.verts.extend(packedboxes.boxes[0].v)
|
||||
|
||||
while unpackedboxes.boxes != []:
|
||||
|
||||
freeBoxIdx = 0
|
||||
while freeBoxIdx < len(unpackedboxes.boxes):
|
||||
|
||||
# Sort the verts with this boxes dimensions as a bias, so less poky out bits are made.
|
||||
packedVerts.sortCorner(unpackedboxes.boxes[freeBoxIdx].width, unpackedboxes.boxes[freeBoxIdx].height)
|
||||
|
||||
vertIdx = 0
|
||||
|
||||
while vertIdx < len(packedVerts.verts):
|
||||
baseVert = packedVerts.verts[vertIdx]
|
||||
|
||||
if baseVert.free != 0:
|
||||
# This will lock the box if its possibel
|
||||
if unpackedboxes.boxes[freeBoxIdx].tryVert(packedboxes, baseVert):
|
||||
packedboxes.addBoxPack(unpackedboxes.boxes[freeBoxIdx])
|
||||
unpackedboxes.boxes.pop(freeBoxIdx)
|
||||
freeBoxIdx = -1
|
||||
break
|
||||
|
||||
vertIdx +=1
|
||||
freeBoxIdx +=1
|
||||
self.width = packedboxes.width
|
||||
self.height = packedboxes.height
|
||||
# All boxes as a list - X/Y/WIDTH/HEIGHT
|
||||
def list(self):
|
||||
ls = []
|
||||
for b in self.boxes:
|
||||
ls.append( (b.id, b.getLeft(), b.getBottom(), b.width, b.height ) )
|
||||
return ls
|
||||
|
||||
|
||||
''' Define all globals here '''
|
||||
# vert IDX's, make references easier to understand.
|
||||
BL = 0; TR = 1; TL = 2; BR = 3
|
||||
|
||||
# iterate through these
|
||||
quadFlagLs = (1,8,4,2)
|
||||
# Flags for vert idx used quads
|
||||
BLF = 1; TRF = 2; TLF = 4; BRF = 8
|
||||
quadFlagLs = (BLF,BRF,TLF,TRF)
|
||||
|
||||
|
||||
# Global vert pool, stores used lists
|
||||
packedVerts = vertList()
|
||||
|
||||
|
||||
# Packs a list w/h's into box types and places then #Iter times
|
||||
def boxPackIter(boxLs, iter=1, draw=0):
|
||||
iterIdx = 0
|
||||
bestArea = None
|
||||
# Iterate over packing the boxes to get the best FIT!
|
||||
while iterIdx < iter:
|
||||
myBoxLs = boxList([])
|
||||
for b in boxLs:
|
||||
myBoxLs.addBox( box(b[1], b[2], b[0]) ) # w/h/id
|
||||
|
||||
myBoxLs.pack()
|
||||
# myBoxLs.draw() # Draw as we go?
|
||||
|
||||
newArea = myBoxLs.packedArea()
|
||||
|
||||
#print 'pack test %s of %s, area:%.2f' % (iterIdx, iter, newArea)
|
||||
|
||||
# First time?
|
||||
if bestArea == None:
|
||||
bestArea = newArea
|
||||
bestBoxLs = myBoxLs
|
||||
elif newArea < bestArea:
|
||||
bestArea = newArea
|
||||
bestBoxLs = myBoxLs
|
||||
iterIdx+=1
|
||||
|
||||
|
||||
if draw:
|
||||
bestBoxLs.draw()
|
||||
|
||||
#print 'best area: %.4f, %.2f%% efficient' % (bestArea, (float(bestBoxLs.boxArea) / (bestArea+0.000001))*100)
|
||||
return bestBoxLs.width, bestBoxLs.height, bestBoxLs.list()
|
||||
# END USER_BOX_PACK_MOD.py
|
||||
|
||||
# ==============================================================
|
||||
|
||||
|
||||
# Box Packer is included for distrobution.
|
||||
#import box_pack_mod
|
||||
#reload(box_pack_mod)
|
||||
import boxpack2d
|
||||
# reload(boxpack2d)
|
||||
|
||||
|
||||
|
||||
@@ -622,24 +135,6 @@ def lineIntersection2D(x1,y1, x2,y2, _x1,_y1, _x2,_y2):
|
||||
else:
|
||||
return None, None
|
||||
|
||||
def triArea(p1, p2, p3):
|
||||
return CrossVecs(p1-p2, p3-p2).length/2
|
||||
|
||||
# IS a point inside our triangle?
|
||||
'''
|
||||
def pointInTri2D(PT, triP1, triP2, triP3):
|
||||
def triArea(p1, p2, p3):
|
||||
return CrossVecs(p1-p2, p3-p2).length /2
|
||||
area1 = triArea(PT, triP2, triP3)
|
||||
area2 = triArea(PT, triP1, triP3)
|
||||
area3 = triArea(PT, triP1, triP2)
|
||||
triArea = triArea(triP1, triP2, triP3)
|
||||
if area1 + area2 + area3 > triArea+0.01:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
'''
|
||||
|
||||
dict_matrix = {}
|
||||
|
||||
def pointInTri2D(v, v1, v2, v3):
|
||||
@@ -655,13 +150,13 @@ def pointInTri2D(v, v1, v2, v3):
|
||||
side1 = v2 - v1
|
||||
side2 = v3 - v1
|
||||
|
||||
nor = Mathutils.CrossVecs(side1, side2)
|
||||
nor = CrossVecs(side1, side2)
|
||||
|
||||
l1 = [side1[0], side1[1], side1[2]]
|
||||
l2 = [side2[0], side2[1], side2[2]]
|
||||
l3 = [nor[0], nor[1], nor[2]]
|
||||
|
||||
mtx = Mathutils.Matrix(l1, l2, l3)
|
||||
mtx = Matrix(l1, l2, l3)
|
||||
|
||||
# Zero area 2d tri, even tho we throw away zerop area faces
|
||||
# the projection UV can result in a zero area UV.
|
||||
@@ -673,24 +168,17 @@ def pointInTri2D(v, v1, v2, v3):
|
||||
|
||||
dict_matrix[key] = mtx
|
||||
|
||||
if NEW_2_38_MATHUTILS:
|
||||
uvw = (v - v1) * mtx
|
||||
else:
|
||||
uvw = Mathutils.VecMultMat(v - v1, mtx)
|
||||
|
||||
uvw = (v - v1) * mtx
|
||||
return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1
|
||||
|
||||
|
||||
|
||||
def faceArea(f):
|
||||
if len(f) == 3:
|
||||
return triArea(f.v[0].co, f.v[1].co, f.v[2].co)
|
||||
elif len(f) == 4:
|
||||
if len(f.v) == 3:
|
||||
return TriangleArea(f.v[0].co, f.v[1].co, f.v[2].co)
|
||||
elif len(f.v) == 4:
|
||||
return\
|
||||
triArea(f.v[0].co, f.v[1].co, f.v[2].co) +\
|
||||
triArea(f.v[0].co, f.v[2].co, f.v[3].co)
|
||||
|
||||
|
||||
TriangleArea(f.v[0].co, f.v[1].co, f.v[2].co) +\
|
||||
TriangleArea(f.v[0].co, f.v[2].co, f.v[3].co)
|
||||
|
||||
|
||||
def boundsIsland(faces):
|
||||
@@ -730,7 +218,7 @@ def island2Edge(island):
|
||||
edges = {}
|
||||
|
||||
for f in island:
|
||||
for vIdx in range(len(f)):
|
||||
for vIdx in range(len(f.v)):
|
||||
if f.v[vIdx].index > f.v[vIdx-1].index:
|
||||
edges[((f.uv[vIdx-1][0], f.uv[vIdx-1][1]), (f.uv[vIdx][0], f.uv[vIdx][1]))] =\
|
||||
(Vector([f.uv[vIdx-1][0], f.uv[vIdx-1][1]]) - Vector([f.uv[vIdx][0], f.uv[vIdx][1]])).length
|
||||
@@ -794,7 +282,7 @@ def pointInIsland(pt, island):
|
||||
if pointInTri2D(pt, vec1, vec2, vec3):
|
||||
return True
|
||||
|
||||
if len(f) == 4:
|
||||
if len(f.v) == 4:
|
||||
vec1.x, vec1.y = f.uv[0]
|
||||
vec2.x, vec2.y = f.uv[2]
|
||||
vec3.x, vec3.y = f.uv[3]
|
||||
@@ -822,10 +310,7 @@ def islandIntersectUvIsland(source, target, xSourceOffset, ySourceOffset):
|
||||
|
||||
# 1 test for source being totally inside target
|
||||
for pv in source[7]:
|
||||
if NEW_2_38_MATHUTILS:
|
||||
p = Vector(pv)
|
||||
else:
|
||||
p = CopyVec(pv)
|
||||
p = Vector(pv)
|
||||
|
||||
p.x += xSourceOffset
|
||||
p.y += ySourceOffset
|
||||
@@ -834,11 +319,7 @@ def islandIntersectUvIsland(source, target, xSourceOffset, ySourceOffset):
|
||||
|
||||
# 2 test for a part of the target being totaly inside the source.
|
||||
for pv in target[7]:
|
||||
|
||||
if NEW_2_38_MATHUTILS:
|
||||
p = Vector(pv)
|
||||
else:
|
||||
p = CopyVec(pv)
|
||||
p = Vector(pv)
|
||||
|
||||
p.x -= xSourceOffset
|
||||
p.y -= ySourceOffset
|
||||
@@ -861,11 +342,7 @@ def testNewVecLs2DRotIsBetter(vecs, mat=-1, bestAreaSoFar = -1):
|
||||
|
||||
# Do this allong the way
|
||||
if mat != -1:
|
||||
# 2.37 depricated
|
||||
if NEW_2_38_MATHUTILS:
|
||||
v = vecs[i] = v*mat
|
||||
else:
|
||||
v = vecs[i] = VecMultMat(v, mat)
|
||||
v = vecs[i] = v*mat
|
||||
|
||||
minx = min(minx, v.x)
|
||||
maxx = max(maxx, v.x)
|
||||
@@ -971,8 +448,8 @@ def optiRotateUvIsland(faces):
|
||||
# Now write the vectors back to the face UV's
|
||||
i = 0 # count the serialized uv/vectors
|
||||
for f in faces:
|
||||
f.uv = [uv for uv in uvVecs[i:len(f)+i] ]
|
||||
i += len(f)
|
||||
f.uv = tuple([uv for uv in uvVecs[i:len(f.v)+i] ])
|
||||
i += len(f.v)
|
||||
|
||||
|
||||
# Takes an island list and tries to find concave, hollow areas to pack smaller islands into.
|
||||
@@ -1000,7 +477,7 @@ def mergeUvIslands(islandList, islandListArea):
|
||||
while fIdx:
|
||||
fIdx-=1
|
||||
f = islandList[islandIdx][fIdx]
|
||||
f.uv = [(uv[0]-minx, uv[1]-miny) for uv in f.uv]
|
||||
f.uv = tuple([Vector(uv[0]-minx, uv[1]-miny) for uv in f.uv])
|
||||
totFaceArea += islandListArea[islandIdx][fIdx] # Use Cached area. dont recalculate.
|
||||
islandBoundsArea = w*h
|
||||
efficiency = abs(islandBoundsArea - totFaceArea)
|
||||
@@ -1119,26 +596,16 @@ def mergeUvIslands(islandList, islandListArea):
|
||||
'''
|
||||
# Move the test allong its width + SMALL_NUM
|
||||
boxLeft += sourceIsland[4] + SMALL_NUM
|
||||
#py_sys.stdout.write('>')
|
||||
#pass
|
||||
elif Intersect == 0: # No intersection?? Place it.
|
||||
# Progress
|
||||
removedCount +=1
|
||||
Window.DrawProgressBar(0.0, 'Merged: %i islands, Ctrl to finish early.' % removedCount)
|
||||
|
||||
'''
|
||||
if py_sys:
|
||||
py_sys.stdout.write('#')
|
||||
py_sys.stdout.flush()
|
||||
else:
|
||||
print '#'
|
||||
'''
|
||||
|
||||
# Move faces into new island and offset
|
||||
targetIsland[0].extend(sourceIsland[0])
|
||||
while sourceIsland[0]:
|
||||
f = sourceIsland[0].pop()
|
||||
f.uv = [(uv[0]+boxLeft, uv[1]+boxBottom) for uv in f.uv]
|
||||
f.uv = tuple([Vector(uv[0]+boxLeft, uv[1]+boxBottom) for uv in f.uv])
|
||||
|
||||
# Move edge loop into new and offset.
|
||||
# targetIsland[6].extend(sourceIsland[6])
|
||||
@@ -1179,19 +646,12 @@ def mergeUvIslands(islandList, islandListArea):
|
||||
areaIslandIdx+=1
|
||||
|
||||
# Remove empty islands
|
||||
# removedCount = 0
|
||||
i = len(islandList)
|
||||
while i:
|
||||
i-=1
|
||||
if not islandList[i]:
|
||||
islandList.pop(i)
|
||||
# removedCount+=1
|
||||
islandList.pop(i) # Can increment islands removed here.
|
||||
|
||||
# Dont need to return anything
|
||||
# if py_sys: py_sys.stdout.flush()
|
||||
|
||||
# print ''
|
||||
# print removedCount, 'merged'
|
||||
|
||||
# Takes groups of faces. assumes face groups are UV groups.
|
||||
def packLinkedUvs(faceGroups, faceGroupsArea, me):
|
||||
@@ -1254,7 +714,7 @@ def packLinkedUvs(faceGroups, faceGroupsArea, me):
|
||||
for v in newIsland[0].v:
|
||||
popoffset = 0
|
||||
for fIdx in xrange(len(faceUsers[v.index])):
|
||||
if faceUsers[v.index][fIdx - popoffset] == newIsland[0]:
|
||||
if faceUsers[v.index][fIdx - popoffset] is newIsland[0]:
|
||||
faceUsers[v.index].pop(fIdx - popoffset)
|
||||
faceUsersArea[v.index].pop(fIdx - popoffset)
|
||||
|
||||
@@ -1276,7 +736,7 @@ def packLinkedUvs(faceGroups, faceGroupsArea, me):
|
||||
#faceUsers = [f for f in faceUsers[vv.index] if f != sharedFace]
|
||||
fIdx = 0
|
||||
for fIdx in xrange(len(faceUsers[vv.index])):
|
||||
if faceUsers[vv.index][fIdx] == sharedFace:
|
||||
if faceUsers[vv.index][fIdx] is sharedFace:
|
||||
faceUsers[vv.index].pop(fIdx)
|
||||
faceUsersArea[vv.index].pop(fIdx)
|
||||
break # Can only be used once.
|
||||
@@ -1291,15 +751,7 @@ def packLinkedUvs(faceGroups, faceGroupsArea, me):
|
||||
|
||||
else:
|
||||
print '\t(empty island found, ignoring)'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#Window.DrawProgressBar(0.1, 'Found %i UV Islands' % len(islandList))
|
||||
#print '\n\tFound %i UV Islands' % len(islandList)
|
||||
|
||||
#print '\tOptimizing Island Rotation...'
|
||||
|
||||
Window.DrawProgressBar(0.1, 'Optimizing Rotation for %i UV Islands' % len(islandList))
|
||||
|
||||
@@ -1309,9 +761,7 @@ def packLinkedUvs(faceGroups, faceGroupsArea, me):
|
||||
|
||||
|
||||
if USER_FILL_HOLES:
|
||||
Window.DrawProgressBar(0.1, 'Merging Islands...')
|
||||
#print '\tMerging islands to save space ("#" == one merge):\n',
|
||||
if py_sys: py_sys.stdout.flush()
|
||||
Window.DrawProgressBar(0.1, 'Merging Islands (Ctrl: skip merge)...')
|
||||
mergeUvIslands(islandList, islandListArea) # Modify in place
|
||||
|
||||
|
||||
@@ -1354,7 +804,7 @@ def packLinkedUvs(faceGroups, faceGroupsArea, me):
|
||||
Window.DrawProgressBar(0.7, 'Packing %i UV Islands...' % len(boxes2Pack) )
|
||||
|
||||
time1 = sys.time()
|
||||
packWidth, packHeight, packedLs = boxPackIter(boxes2Pack)
|
||||
packWidth, packHeight, packedLs = boxpack2d.boxPackIter(boxes2Pack)
|
||||
# print 'Box Packing Time:', sys.time() - time1
|
||||
|
||||
#if len(pa ckedLs) != len(islandList):
|
||||
@@ -1367,8 +817,12 @@ def packLinkedUvs(faceGroups, faceGroupsArea, me):
|
||||
islandIdx = len(islandList)
|
||||
# Having these here avoids devide by 0
|
||||
if islandIdx:
|
||||
xfactor = 1.0 / packWidth
|
||||
yfactor = 1.0 / packHeight
|
||||
# Maximize to uv area?? Will write a normalize function.
|
||||
#xfactor = 1.0 / packWidth
|
||||
#yfactor = 1.0 / packHeight
|
||||
|
||||
# Keep proportions.
|
||||
xfactor = yfactor = 1.0 / max(packWidth, packHeight)
|
||||
|
||||
while islandIdx:
|
||||
islandIdx -=1
|
||||
@@ -1378,14 +832,11 @@ def packLinkedUvs(faceGroups, faceGroupsArea, me):
|
||||
xoffset = packedLs[islandIdx][1] - islandOffsetList[islandIdx][0]
|
||||
yoffset = packedLs[islandIdx][2] - islandOffsetList[islandIdx][1]
|
||||
for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box
|
||||
f.uv = [(((uv[0]+xoffset)*xfactor), ((uv[1]+yoffset)*yfactor)) for uv in f.uv]
|
||||
f.uv = tuple([Vector(((uv[0]+xoffset)*xfactor), ((uv[1]+yoffset)*yfactor)) for uv in f.uv])
|
||||
|
||||
def VectoMat(vec):
|
||||
|
||||
if NEW_2_38_MATHUTILS:
|
||||
a3 = Vector(vec)
|
||||
else:
|
||||
a3 = CopyVec(vec)
|
||||
a3 = Vector(vec)
|
||||
|
||||
a3.normalize()
|
||||
|
||||
@@ -1442,11 +893,11 @@ def main():
|
||||
if ob.getType() != 'Mesh':
|
||||
continue
|
||||
|
||||
me = ob.getData()
|
||||
me = ob.getData(mesh=1)
|
||||
if USER_ONLY_SELECTED_FACES:
|
||||
meshFaces = [f for f in me.getSelectedFaces() if len(f) > 2]
|
||||
meshFaces = [f for f in me.faces if f.sel if len(f.v) > 2]
|
||||
else:
|
||||
meshFaces = [f for f in me.faces if len(f) > 2]
|
||||
meshFaces = [f for f in me.faces if len(f.v) > 2]
|
||||
|
||||
if not meshFaces:
|
||||
continue
|
||||
@@ -1467,7 +918,7 @@ def main():
|
||||
for f in meshFaces:
|
||||
area = faceArea(f)
|
||||
if area <= SMALL_NUM:
|
||||
f.uv = [(0.0, 0.0)] * len(f)
|
||||
f.uv = [(0.0, 0.0)] * len(f.v)
|
||||
print 'found zero area face, removing.'
|
||||
|
||||
else:
|
||||
@@ -1573,9 +1024,7 @@ def main():
|
||||
i = len(projectVecs)
|
||||
|
||||
# Initialize first
|
||||
|
||||
bestAng = DotVecs(fvec, projectVecs[0])
|
||||
# print bestAng
|
||||
bestAngIdx = 0
|
||||
|
||||
# Cycle through the remaining, first alredy done
|
||||
@@ -1609,26 +1058,25 @@ def main():
|
||||
|
||||
# Get the faces UV's from the projected vertex.
|
||||
for f in faceProjectionGroupList[i]:
|
||||
|
||||
if NEW_2_38_MATHUTILS:
|
||||
f.uv = [MatProj * v.co for v in f.v]
|
||||
else:
|
||||
f.uv = [MatMultVec(MatProj, v.co) for v in f.v]
|
||||
f.uv = tuple([MatProj * v.co for v in f.v])
|
||||
|
||||
packLinkedUvs(faceProjectionGroupList, faceProjectionGroupListArea, me)
|
||||
|
||||
#print "ArchiMap time: %.2f" % (sys.time() - time1)
|
||||
print "ArchiMap time: %.2f" % (sys.time() - time1)
|
||||
Window.DrawProgressBar(0.9, "ArchiMap Done, time: %.2f sec." % (sys.time() - time1))
|
||||
|
||||
# Update and dont mess with edge data.
|
||||
me.update(0, (me.edges != []), 0)
|
||||
# OLD NMESH # me.update(0, (me.edges != []), 0)
|
||||
|
||||
Window.DrawProgressBar(1.0, "")
|
||||
Window.WaitCursor(0)
|
||||
Window.RedrawAll()
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print '\nUser Canceled.'
|
||||
Draw.PupMenu('user canceled execution, unwrap aborted.')
|
||||
|
||||
Window.WaitCursor(0)
|
||||
Window.DrawProgressBar(1.0, "")
|
||||
Window.WaitCursor(0)
|
||||
Reference in New Issue
Block a user