コード例 #1
0
	def shapeMatchIndexes(self, indexes):
		# make a dict of name:object
		sel = DCC.getSelectedObjects()
		if not sel:
			return
		mesh = sel[0]

		pairs = coerceIndexToChildType(indexes, ProgPair)
		pairs = [i.model().itemFromIndex(i) for i in pairs]
		pairs = makeUnique([i for i in pairs if not i.shape.isRest])

		# Set up the progress bar
		pBar = QProgressDialog("Matching Shapes", "Cancel", 0, 100, self)
		pBar.setMaximum(len(pairs))

		# Do the extractions
		for pair in pairs:
			c = pair.prog.controller
			c.connectShape(pair.shape, mesh=mesh)

			# ProgressBar
			pBar.setValue(pBar.value() + 1)
			pBar.setLabelText("Matching:\n{0}".format(pair.shape.name))
			QApplication.processEvents()
			if pBar.wasCanceled():
				return

		pBar.close()
コード例 #2
0
	def exportAbc(self, dccMesh, abcMesh, js, world=False, pBar=None):
		# export the data to alembic
		if dccMesh is None:
			dccMesh = self.mesh

		shapeDict = {i.name:i for i in self.simplex.shapes}

		shapeNames = js['shapes']
		if js['encodingVersion'] > 1:
			shapeNames = [i['name'] for i in shapeNames]
		shapes = [shapeDict[i] for i in shapeNames]

		schema = abcMesh.getSchema()

		if pBar is not None:
			pBar.show()
			pBar.setMaximum(len(shapes))
			spacerName = '_' * max(map(len, shapeNames))
			pBar.setLabelText('Exporting:\n{0}'.format(spacerName))
			QApplication.processEvents()

		for i, shape in enumerate(shapes):
			if pBar is not None:
				pBar.setLabelText('Exporting:\n{0}'.format(shape.name))
				pBar.setValue(i)
				QApplication.processEvents()
				if pBar.wasCanceled():
					return
			verts = mkSampleVertexPoints(self._shapes[shape.name])
			if self._uvs is not None:
				# Alembic doesn't allow for self._uvs=None for some reason
				abcSample = OPolyMeshSchemaSample(verts, self._faces, self._counts, self._uvs)
			else:
				abcSample = OPolyMeshSchemaSample(verts, self._faces, self._counts)
			schema.set(abcSample)
コード例 #3
0
def readAndApplyCorrectives(inPath, namePath, refPath, outPath, pBar=None):
	'''
	Read the provided files, apply the correctives, then output a new file

	Arguments:
		inPath: The input .smpx file
		namePath: A file correlating the shape names, and the reference
			indices. Separated by ; with one entry per line
		refPath: The reference matrices per point of deformation.
			Created by npArray.dump(refPath)
		outPath: The output .smpx filepath
	'''

	if pBar is not None:
		pBar.setLabelText("Reading reference data")
		QApplication.processEvents()

	jsString, simplex, solver, allShapePts, restPts = loadSimplex(inPath)
	with open(namePath, 'r') as f:
		nr = f.read()
	nr = [i.split(';') for i in nr.split('\n') if i]
	names, refIdxs = zip(*nr)
	refIdxs = map(int, refIdxs)
	refs = np.load(refPath)
	shapeByName = {i.name: i for i in simplex.shapes}
	shapes = [shapeByName[n] for n in names]
	newPts = applyCorrectives(simplex, allShapePts, restPts, solver, shapes, refIdxs, refs, pBar)
	writeSimplex(inPath, outPath, newPts, pBar=pBar)
	print "DONE"
コード例 #4
0
def _writeSimplex(oarch, name, jsString, faces, counts, newShapes, pBar=None):
	''' Separate the writer from oarch creation so garbage
	collection *hopefully* works as expected
	'''
	par = OXform(oarch.getTop(), name)
	props = par.getSchema().getUserProperties()
	prop = OStringProperty(props, "simplex")
	prop.setValue(str(jsString))
	abcMesh = OPolyMesh(par, name)
	schema = abcMesh.getSchema()

	if pBar is not None:
		pBar.setLabelText('Writing Corrected Simplex')
		pBar.setMaximum(len(newShapes))

	for i, newShape in enumerate(newShapes):
		if pBar is not None:
			pBar.setValue(i)
			QApplication.processEvents()
		else:
			print "Writing {0: 3d} of {1}\r".format(i, len(newShapes)),

		verts = mkSampleVertexPoints(newShape)
		abcSample = OPolyMeshSchemaSample(verts, faces, counts)
		schema.set(abcSample)
	if pBar is None:
		print "Writing {0: 3d} of {1}".format(len(newShapes), len(newShapes))
コード例 #5
0
def outputCorrectiveReferences(outNames, outRefs, simplex, mesh, poses, sliders, pBar=None):
	'''
	Output the proper files for an external corrective application

	Arguments:
		outNames: The filepath for the output shape and reference indices
		outRefs: The filepath for the deformation references
		simplex: A simplex system
		mesh: The mesh object to deform
		poses: Lists of parameter/value pairs. Each list corresponds to a slider
		sliders: The simplex sliders that correspond to the poses
	'''
	refs, shapes, refIdxs = buildCorrectiveReferences(mesh, simplex, poses, sliders, pBar)

	if pBar is not None:
		pBar.setLabelText('Writing Names')
		QApplication.processEvents()
	nameWrite = ['{};{}'.format(s.name, r) for s, r, in zip(shapes, refIdxs)]
	with open(outNames, 'w') as f:
		f.write('\n'.join(nameWrite))

	if pBar is not None:
		pBar.setLabelText('Writing References')
		QApplication.processEvents()
	refs.dump(outRefs)
コード例 #6
0
	def shapeIndexExtract(self, indexes, live=None):
		# Create meshes that are possibly live-connected to the shapes
		if live is None:
			live = self.uiLiveShapeConnectionACT.isChecked()

		pairs = coerceIndexToChildType(indexes, ProgPair)
		pairs = [i.model().itemFromIndex(i) for i in pairs]
		pairs = makeUnique([i for i in pairs if not i.shape.isRest])
		pairs.sort(key=lambda x: naturalSortKey(x.shape.name))

		# Set up the progress bar
		pBar = QProgressDialog("Extracting Shapes", "Cancel", 0, 100, self)
		pBar.setMaximum(len(pairs))

		# Do the extractions
		offset = 10
		for pair in pairs:
			c = pair.prog.controller
			c.extractShape(pair.shape, live=live, offset=offset)
			offset += 5

			# ProgressBar
			pBar.setValue(pBar.value() + 1)
			pBar.setLabelText("Extracting:\n{0}".format(pair.shape.name))
			QApplication.processEvents()
			if pBar.wasCanceled():
				return

		pBar.close()
コード例 #7
0
def exportUnsub(inPath, outPath, newFaces, kept, shapePrefix=None, pBar=None):
    ''' Export the unsubdivided simplex '''
    iarch = IArchive(str(inPath))  # because alembic hates unicode
    top = iarch.getTop()
    ixfo = IXform(top, top.children[0].getName())

    iprops = ixfo.getSchema().getUserProperties()
    iprop = iprops.getProperty("simplex")
    jsString = iprop.getValue()

    if shapePrefix is not None:
        d = json.loads(jsString)
        if d['encodingVersion'] > 1:
            for shape in d['shapes']:
                shape['name'] = shapePrefix + shape['name']
        else:
            d['shapes'] = [shapePrefix + i for i in d['shapes']]
        jsString = json.dumps(d)

    imesh = IPolyMesh(ixfo, ixfo.children[0].getName())

    verts = getSampleArray(imesh)
    verts = verts[:, kept]

    indices = []
    counts = []
    for f in newFaces:
        indices.extend(f)
        counts.append(len(f))

    abcCounts = mkArray(IntArray, counts)
    abcIndices = mkArray(IntArray, indices)

    # `False` for HDF5 `True` for Ogawa
    oarch = OArchive(str(outPath), False)
    oxfo = OXform(oarch.getTop(), ixfo.getName())
    oprops = oxfo.getSchema().getUserProperties()
    oprop = OStringProperty(oprops, "simplex")
    oprop.setValue(str(jsString))
    omesh = OPolyMesh(oxfo, imesh.getName())
    osch = omesh.getSchema()

    if pBar is not None:
        pBar.setValue(0)
        pBar.setMaximum(len(verts))
        pBar.setLabelText("Exporting Unsubdivided Shapes")
        QApplication.processEvents()

    for i, v in enumerate(verts):
        if pBar is not None:
            pBar.setValue(i)
            QApplication.processEvents()
        else:
            print "Exporting Unsubdivided Shape {0: <4}\r".format(i + 1),
        sample = OPolyMeshSchemaSample(mkSampleVertexPoints(v), abcIndices,
                                       abcCounts)
        osch.set(sample)
    if pBar is None:
        print "Exporting Unsubdivided Shape {0: <4}".format(len(verts))
コード例 #8
0
def buildFullShapes(simplex, shapeObjs, shapes, solver, restPts, pBar=None):
	'''
	Given shape inputs, build the full output shape from the deltas
	We use shapes here because a shape implies both the progression
	and the value of the inputs (with a little figuring)
	'''
	###########################################
	# Manipulate all the input lists and caches
	indexBySlider = {s: i for i, s in enumerate(simplex.sliders)}
	indexByShape = {s: i for i, s in enumerate(simplex.shapes)}
	floaters = set(simplex.getFloatingShapes())
	floatIdxs = set([indexByShape[s] for s in floaters])

	shapeDict = {}
	for item in itertools.chain(simplex.sliders, simplex.combos):
		for pair in item.prog.pairs:
			if not pair.shape.isRest:
				shapeDict[pair.shape] = (item, pair.value)

	######################
	# Actually do the work
	vecByShape = {} # store this for later use
	ptsByShape = {}

	if pBar is not None:
		pBar.setMaximum(len(shapeObjs))
		pBar.setValue(0)
		QApplication.processEvents()

	flatShapes = shapes.reshape((len(shapes), -1))
	for i, shape in enumerate(shapeObjs):
		if pBar is not None:
			pBar.setValue(i)
			QApplication.processEvents()
		else:
			print "Building {0} of {1}\r".format(i+1, len(shapeObjs)),

		item, value = shapeDict[shape]
		inVec = _buildSolverInputs(simplex, item, value, indexBySlider)
		outVec = solver.solve(inVec)
		if shape not in floaters:
			for fi in floatIdxs:
				outVec[fi] = 0.0
		outVec = np.array(outVec)
		outVec[np.where(np.isclose(outVec, 0))] = 0
		outVec[np.where(np.isclose(outVec, 1))] = 1
		vecByShape[shape] = outVec
		pts = np.dot(outVec, flatShapes)
		pts = pts.reshape((-1, 3))
		ptsByShape[shape] = pts + restPts
	if pBar is None:
		print

	return ptsByShape, vecByShape
コード例 #9
0
	def importSystemFromFile(self):
		if self._currentObject is None:
			impTypes = ['smpx']
		else:
			impTypes = ['smpx', 'json']

		if blurdev is None:
			pref = QSettings("Blur", "Simplex3")
			defaultPath = str(toPyObject(pref.value('systemImport', os.path.join(os.path.expanduser('~')))))
			path = self.fileDialog("Import Template", defaultPath, impTypes, save=False)
			if not path:
				return
			pref.setValue('systemImport', os.path.dirname(path))
			pref.sync()
		else:
			# Blur Prefs
			pref = blurdev.prefs.find('tools/simplex3')
			defaultPath = pref.restoreProperty('systemImport', os.path.join(os.path.expanduser('~')))
			path = self.fileDialog("Import Template", defaultPath, impTypes, save=False)
			if not path:
				return
			pref.recordProperty('systemImport', os.path.dirname(path))
			pref.save()

		pBar = QProgressDialog("Loading Shapes", "Cancel", 0, 100, self)
		pBar.show()
		QApplication.processEvents()

		# TODO: Come up with a better list of possibilites for loading
		# simplex files, and make the appropriate methods on the Simplex
		if path.endswith('.smpx'):
			newSystem = Simplex.buildSystemFromSmpx(path, self._currentObject, sliderMul=self._sliderMul, pBar=pBar)
		elif path.endswith('.json'):
			newSystem = Simplex.buildSystemFromJson(path, self._currentObject, sliderMul=self._sliderMul, pBar=pBar)
		
		with signalsBlocked(self.uiCurrentSystemCBOX):
			self.loadObject(newSystem.DCC.mesh)
			idx = self.uiCurrentSystemCBOX.findText(self._currentObjectName)
			if idx >= 0:
				self.uiCurrentSystemCBOX.setCurrentIndex(idx)
			else:
				self.uiCurrentSystemCBOX.addItem(newSystem.name)
				self.uiCurrentSystemCBOX.setCurrentIndex(self.uiCurrentSystemCBOX.count()-1)

		self.setSystem(newSystem)
		pBar.close()
コード例 #10
0
def applyCorrectives(simplex, allShapePts, restPts, solver, shapes, refIdxs, references, pBar=None):
	'''
	Loop over the shapes and references, apply them, and return a new np.array
	of shape points

	simplex: Simplex system
	allShapePts: deltas per shape
	restPts: The rest point positions
	solver: The Python Simplex solver object
	shapes: The simplex shape objects we care about
	refIdxs: The reference index per shape
	references: A list of matrix-per-points
	'''
	# The rule of thumb is "THE SHAPE IS ALWAYS A DELTA"

	if pBar is not None:
		pBar.setLabelText("Inverting References")
		pBar.setValue(0)
		pBar.setMaximum(len(references))
		QApplication.processEvents()
	else:
		print "Inverting References"

	inverses = []
	for i, r in enumerate(references):
		if pBar is not None:
			pBar.setValue(i)
			QApplication.processEvents()
		inverses.append(invertAll(r))

	if pBar is not None:
		pBar.setLabelText("Extracting Uncorrected Shapes")
		QApplication.processEvents()
	else:
		print "Building Full Shapes"
	ptsByShape, vecByShape = buildFullShapes(simplex, shapes, allShapePts, solver, restPts, pBar)

	if pBar is not None:
		pBar.setLabelText("Correcting")
		QApplication.processEvents()
	else:
		print "Correcting"
	newPtsByShape = {}
	for shape, refIdx in zip(shapes, refIdxs):
		inv = inverses[refIdx]
		pts = ptsByShape[shape]
		newPts = applyReference(pts, inv)
		newPtsByShape[shape] = newPts

	newShapePts = collapseFullShapes(simplex, allShapePts, newPtsByShape, vecByShape, pBar)
	newShapePts = newShapePts + restPts[None, ...]

	return newShapePts
コード例 #11
0
ファイル: traversalDialog.py プロジェクト: sniler/Simplex
    def shapeConnectFromSelection(self):
        if self.simplex is None:
            return
        # make a dict of name:object
        sel = DCC.getSelectedObjects()
        selDict = {}
        for s in sel:
            name = DCC.getObjectName(s)
            if name.endswith("_Extract"):
                nn = name.rsplit("_Extract", 1)[0]
                selDict[nn] = s

        pairDict = {}
        for p in self.simplex.progs:
            for pp in p.pairs:
                pairDict[pp.shape.name] = pp

        # get all common names
        selKeys = set(selDict.iterkeys())
        pairKeys = set(pairDict.iterkeys())
        common = selKeys & pairKeys

        # get those items
        pairs = [pairDict[i] for i in common]

        # Set up the progress bar
        pBar = QProgressDialog("Connecting Shapes", "Cancel", 0, 100, self)
        pBar.setMaximum(len(pairs))

        # Do the extractions
        for pair in pairs:
            c = pair.prog.controller
            c.connectShape(pair.shape, delete=True)

            # ProgressBar
            pBar.setValue(pBar.value() + 1)
            pBar.setLabelText("Connecting:\n{0}".format(pair.shape.name))
            QApplication.processEvents()
            if pBar.wasCanceled():
                return

        pBar.close()
コード例 #12
0
	def shapeConnectIndexes(self, indexes):
		pairs = coerceIndexToChildType(indexes, ProgPair)
		pairs = [i.model().itemFromIndex(i) for i in pairs]
		pairs = makeUnique([i for i in pairs if not i.shape.isRest])

		# Set up the progress bar
		pBar = QProgressDialog("Connecting Shapes", "Cancel", 0, 100, self)
		pBar.setMaximum(len(pairs))

		# Do the extractions
		for pair in pairs:
			c = pair.prog.controller
			c.connectShape(pair.shape, delete=True)

			# ProgressBar
			pBar.setValue(pBar.value() + 1)
			pBar.setLabelText("Extracting:\n{0}".format(pair.shape.name))
			QApplication.processEvents()
			if pBar.wasCanceled():
				return

		pBar.close()
コード例 #13
0
def collapseFullShapes(simplex, allPts, ptsByShape, vecByShape, pBar=None):
	'''
	Given a set of shapes that are full-on shapes (not just deltas)
	Collapse them back into deltas in the simplex shape list
	'''
	#######################
	# Manipulate all the input lists and caches
	#indexBySlider = {s: i for i, s in enumerate(simplex.sliders)}
	indexByShape = {s: i for i, s in enumerate(simplex.shapes)}
	floaters = set(simplex.getFloatingShapes())
	#floatIdxs = set([indexByShape[s] for s in floaters])
	newPts = np.copy(allPts)

	# Order the combos by depth, and split out the floaters
	allDFirst = sorted(simplex.combos[:], key=lambda x: len(x.pairs))
	dFirst, dFloat = [], []
	for c in allDFirst:
		app = dFloat if c.isFloating() else dFirst
		app.append(c)

	# first do the sliders
	for item in simplex.sliders:
		for pair in item.prog.pairs:
			if pair.shape in ptsByShape:
				idx = indexByShape[pair.shape]
				newPts[idx] = ptsByShape[pair.shape]

	# Get the max number of iterations
	mxcount = 0
	for c in itertools.chain(dFirst, dFloat):
		for pair in c.prog.pairs:
			if pair.shape in ptsByShape:
				mxcount += 1

	if pBar is not None:
		pBar.setValue(0)
		pBar.setMaximum(mxcount)
		pBar.setLabelText("Building Corrected Deltas")
		QApplication.processEvents()

	# Then go through all the combos in order
	vcount = 0
	for c in itertools.chain(dFirst, dFloat):

		for pair in c.prog.pairs:
			if pair.shape in ptsByShape:
				if pBar is not None:
					pBar.setValue(vcount)
					QApplication.processEvents()
				else:
					print "Collapsing {0} of {1}\r".format(vcount + 1, mxcount),
				vcount += 1

				idx = indexByShape[pair.shape]
				outVec = vecByShape[pair.shape]
				outVec[idx] = 0.0 # turn off the influence of the current shape
				comboBase = np.dot(outVec, newPts.transpose((1, 0, 2)))
				comboSculpt = ptsByShape[pair.shape]
				newPts[idx] = comboSculpt - comboBase
	if pBar is None:
		print

	return newPts
コード例 #14
0
def buildCorrectiveReferences(mesh, simplex, poses, sliders, pBar=None):
	'''
	Take correlated poses and sliders, and expand down the
	simplex combo tree, building references for each required shape

	Inputs:
		simplex <SimplexSystem> : A simplex system
		solver <PySimplex> : An instantiated simplex value solver
		poses <Prop/Value pair lists> : Different rig poses
		shapes <SimplexShapes> : Shape objects correlated to the poses
	'''
	# cache the pose search

	# Pre-cache the combo search
	allCombosBySliderValue = {}
	for c in simplex.combos:
		for p in c.pairs:
			allCombosBySliderValue.setdefault((p.slider, p.value), []).append(c)

	# This is only my subset set of downstreams
	# Get the downstreams by slider and value
	sliderValuesByCombo = {}
	for slider in sliders:
		for p in slider.prog.pairs:
			combos = allCombosBySliderValue.get((slider, p.value), [])
			for combo in combos:
				sliderValuesByCombo.setdefault(combo, []).append((slider, p.value))

	#out = []
	refCache = {}
	refs, shapes, refIdxs = [], [], []

	# get the slider outputs
	if pBar is not None:
		pBar.setLabelText("Building Shape References")
		pBar.setValue(0)
		mv = 0
		for slider in sliders:
			for p in slider.prog.pairs:
				if not p.shape.isRest:
					mv += 1
		pBar.setMaximum(mv)
		QApplication.processEvents()

	poseBySlider = {}
	for slider, pose in zip(sliders, poses):
		poseBySlider[slider] = pose
		for p in slider.prog.pairs:
			if not p.shape.isRest:
				if pBar is not None:
					pBar.setValue(pBar.value())
					QApplication.processEvents()
				cacheKey = frozenset([(slider, p.value)])
				if cacheKey in refCache:
					idx = refCache[cacheKey]
					refIdxs.append(idx)
				else:
					ref = getRefForPoses(mesh, [pose], p.value)
					refIdxs.append(len(refs))
					refCache[cacheKey] = len(refs)
					refs.append(ref)
				shapes.append(p.shape)

	# Get the combo outputs
	if pBar is not None:
		pBar.setLabelText("Building Combo References")
		pBar.setValue(0)
		mv = 0
		for combo in sliderValuesByCombo.iterkeys():
			for p in combo.prog.pairs:
				if not p.shape.isRest:
					mv += 1
		pBar.setMaximum(mv)
		QApplication.processEvents()

	for combo, sliderVals in sliderValuesByCombo.iteritems():
		#components = frozenset(sliderVals)
		poses = [poseBySlider[s] for s, _ in sliderVals]
		for p in combo.prog.pairs:
			if not p.shape.isRest:
				if pBar is not None:
					pBar.setValue(pBar.value())
					QApplication.processEvents()

				cacheKey = frozenset(sliderVals)
				if cacheKey in refCache:
					idx = refCache[cacheKey]
					refIdxs.append(idx)
				else:
					ref = getRefForPoses(mesh, poses, p.value)
					refIdxs.append(len(refs))
					refCache[cacheKey] = len(refs)
					refs.append(ref)
				shapes.append(p.shape)

	return np.array(refs), shapes, refIdxs
コード例 #15
0
	def importObjFolder(self, folder):
		''' Import all objs from a user selected folder '''
		if not os.path.isdir(folder):
			QMessageBox.warning(self, 'Warning', 'Folder does not exist')
			return

		paths = os.listdir(folder)
		paths = [i for i in paths if i.endswith('.obj')]
		if not paths:
			QMessageBox.warning(self, 'Warning', 'Folder does not contain any .obj files')
			return

		shapeDict = {shape.name: shape for shape in self.simplex.shapes}

		inPairs = {}
		for path in paths:
			shapeName = os.path.splitext(os.path.basename(path))[0]
			shape = shapeDict.get(shapeName)
			if shape is not None:
				inPairs[shapeName] = path
			else:
				sfx = "_Extract"
				if shapeName.endswith(sfx):
					shapeName = shapeName[:-len(sfx)]
					shape = shapeDict.get(shapeName)
					if shape is not None:
						inPairs[shapeName] = path

		sliderMasters, comboMasters = {}, {}
		for masters in [self.simplex.sliders, self.simplex.combos]:
			for master in masters:
				for pp in master.prog.pairs:
					shape = shapeDict.get(pp.shape.name)
					if shape is not None:
						if shape.name in inPairs:
							if isinstance(master, Slider):
								sliderMasters[shape.name] = master
							if isinstance(master, Combo):
								comboMasters[shape.name] = master

		comboDepth = {}
		for k, v in comboMasters.iteritems():
			depth = len(v.pairs)
			comboDepth.setdefault(depth, {})[k] = v

		pBar = QProgressDialog("Loading from Mesh", "Cancel", 0, len(comboMasters) + len(sliderMasters), self)
		pBar.show()

		for shapeName, slider in sliderMasters.iteritems():
			pBar.setValue(pBar.value() + 1)
			pBar.setLabelText("Loading Obj :\n{0}".format(shapeName))
			QApplication.processEvents()
			if pBar.wasCanceled():
				return

			path = inPairs[shapeName]
			mesh = self.simplex.DCC.importObj(os.path.join(folder, path))

			shape = shapeDict[shapeName]
			self.simplex.DCC.connectShape(shape, mesh=mesh, live=False, delete=True)

		for depth in sorted(comboDepth.keys()):
			for shapeName, combo in comboDepth[depth].iteritems():
				pBar.setValue(pBar.value() + 1)
				pBar.setLabelText("Loading Obj :\n{0}".format(shapeName))
				QApplication.processEvents()
				if pBar.wasCanceled():
					return

				path = inPairs[shapeName]
				mesh = self.simplex.DCC.importObj(os.path.join(folder, path))
				shape = shapeDict[shapeName]
				self.simplex.DCC.connectComboShape(combo, shape, mesh=mesh, live=False, delete=True)

		pBar.close()