赞
踩
上周部门内部进行分享知识讲座时讲到一个词Voxelization,也就是体素化,也就是用某些东西充满一个物体的内部,如下面这两张图,用小方块充满圆环和锥体。
其主要算法也有多种,我这里采用的是用boundingbox来切割物体的方法,如下图:
就是先求出一个模型的boundingbox,然后以一定的step对boundingbox的长宽高进行切割,然后从这些黄色的交点,按照某一方向,向模型打点,如果是奇数个点就证明是在模型内部,然后在这个坐标位置处创建个box或其他的什么模型都行,偶数个点则证明在模型外部,不予处理就行。
下面这个是Voxel的生成器,需要输入三个参数: mesh, typeIndex, density
mesh要体素化的模型
typeIndex是world还是local模式
density是创建盒子的密度,默认是2,不能低于2
import maya.OpenMaya as om import maya.OpenMayaMPx as omp class VoxelGenerator(object): World = 0 Local = 1 def __init__(self, inputMesh, typeIndex, density): self.density = density self.typeIndex = typeIndex self.meshDagPath = om.MDagPath.getAPathTo(inputMesh) self.mexclusive = self.meshDagPath.exclusiveMatrix() self.borderOffset = 0.01 self.voxelObjects = om.MObjectArray() self.bbox = self.getBoundingBox() self.xlen = self.bbox.max().x - self.bbox.min().x self.ylen = self.bbox.max().y - self.bbox.min().y self.zlen = self.bbox.max().z - self.bbox.min().z minEdge = self.xlen if minEdge > self.ylen: minEdge = self.ylen if minEdge > self.zlen: minEdge = self.zlen self.step = (minEdge - 2 * minEdge * self.borderOffset) / (self.density - 1.0) def decimalRange(self, start, end, step): while start < end: yield start start += step def pointToMObject(self, name): selectionList= om.MSelectionList() _mobject = om.MObject() selectionList.add(name) selectionList.getDependNode(0, _mobject) return _mobject def generateVoxel(self): outerPoint = om.MFloatPoint(self.bbox.max().x * 2, self.bbox.max().y * 2, self.bbox.max().z * 2) mfnMesh = om.MFnMesh(self.meshDagPath) transformationMatrix = om.MTransformationMatrix(self.mexclusive) rotation = transformationMatrix.rotation() rayDirection = om.MFloatVector(outerPoint) faceIds = None triIds = None idsSorted = False space = om.MSpace.kWorld maxParam = 999999 testBothDirections = False accelParams = None sortHits = True hitPoints = om.MFloatPointArray() hitRayParams = om.MFloatArray() hitFaces = om.MIntArray() voxelElement_mfnTransform = om.MFnTransform() dagModifier = om.MDagModifier() for x in self.decimalRange((self.bbox.min().x + self.xlen * self.borderOffset), self.bbox.max().x, self.step): for y in self.decimalRange((self.bbox.min().y + self.ylen * self.borderOffset), self.bbox.max().y, self.step): for z in self.decimalRange((self.bbox.min().z + self.zlen * self.borderOffset), self.bbox.max().z, self.step): point = om.MPoint(x, y, z) if self.typeIndex == VoxelGenerator.Local: point = self.localToWorldSpace(point) fPoint = om.MFloatPoint(point.x, point.y, point.z) hitPoints.clear() mfnMesh.allIntersections(fPoint, rayDirection, faceIds, triIds, idsSorted, space, maxParam, testBothDirections, accelParams, sortHits, hitPoints, hitRayParams, hitFaces, None, None, None, 0.000001) if hitPoints.length() % 2 == 0: continue # voxelElement = dagModifier.createNode('locator') pcube = cmds.polyCube(w=1, h=1, d=1, sx=1, sy=1, sz=1) voxelElement = self.pointToMObject(pcube[0]) voxelElementMDagPath = om.MDagPath.getAPathTo(voxelElement) self.voxelObjects.append(voxelElement) voxelElement_mfnTransform.setObject(voxelElementMDagPath) voxelElement_mfnTransform.setTranslation(om.MVector(fPoint.x, fPoint.y, fPoint.z), om.MSpace.kWorld) if self.typeIndex == VoxelGenerator.Local: voxelElement_mfnTransform.setRotation(rotation) dagModifier.doIt() voxelCount = self.voxelObjects.length() return voxelCount def localToWorldSpace(self, point): mInclusive = self.meshDagPath.inclusiveMatrix() point = point * mInclusive return point def deleteVoxel(self): try: for i in range(self.voxelObjects.length()): om.MGlobal.deleteNode(self.voxelObjects[i]) except Exception as e: print e self.voxelObjects.clear() def getBoundingBox(self): meshDagNode = om.MFnDagNode(self.meshDagPath) bbox = meshDagNode.boundingBox() if self.typeIndex == VoxelGenerator.World: bbox.transformUsing(self.mexclusive) return bbox
测试代码:
if __name__ == '__main__':
selectionList= om.MSelectionList()
_cone= om.MObject()
selectionList.add('pCone1')
selectionList.getDependNode(0, _cone)
vg = VoxelGenerator(_torus, 0, 8)
count = vg.generateVoxel()
print count
# vg.deleteVoxel()
这里说明下,加了borderOffset这个参数的目的主要是让其起始位置不要定在boundingbox的边界上,因为那个肯定不在模型的内部,所以向里略微偏移一点点,然后在对模型进行切割。
从某个点沿着某个方向向模型打点,自己就不多解释了,很常用的算法。
有了这个生成器之后,我们就可以写成一个节点了,当我们调节节点的density的时候,生成的小盒子会实时更新,
import sys import time import maya.OpenMaya as om import maya.OpenMayaMPx as omp kPluginNodeTypeName = 'voxelizer' voxelizerNodeId = om.MTypeId(0x9500) class Voxelizer(omp.MPxNode): voxelType = om.MObject() inputMesh = om.MObject() output = om.MObject() voxelDensity = om.MObject() def __init__(self): omp.MPxNode.__init__(self) self.grid = None def compute(self, plug, dataBlock): meshDataHandle = dataBlock.inputValue(Voxelizer.inputMesh) connectedMesh = meshDataHandle.asMesh() meshApiType = connectedMesh.apiType() if meshApiType == om.MFn.kInvalid: om.MGlobal.displayInfo('No mesh connected') dataBlock.setClean(plug) return False elif meshApiType == om.MFn.kMeshData: start = time.time() # get connected mesh mesh_plug = om.MPlug(self.thisMObject(), Voxelizer.inputMesh) plugs = om.MPlugArray() mesh_plug.connectedTo(plugs, True, True) real_connectedMesh = plugs[0].node() voxelTypeDataHandle = dataBlock.inputValue(Voxelizer.voxelType) voxelType = voxelTypeDataHandle.asShort() densityDataHandle = dataBlock.inputValue(Voxelizer.voxelDensity) density = densityDataHandle.asShort() if self.grid: self.grid.deleteVoxel() self.grid = VoxelGenerator(real_connectedMesh, voxelType, density) voxelCount = self.grid.generateVoxel() outputHandle = dataBlock.outputValue(Voxelizer.output) outputHandle.setInt(voxelCount) end = time.time() om.MGlobal.displayInfo('calculation time: %s' % (end - start)) dataBlock.setClean(plug) return True class VoxelGenerator(object): World = 0 Local = 1 def __init__(self, inputMesh, typeIndex, density): self.density = density self.typeIndex = typeIndex self.meshDagPath = om.MDagPath.getAPathTo(inputMesh) self.mexclusive = self.meshDagPath.exclusiveMatrix() self.borderOffset = 0.01 self.voxelObjects = om.MObjectArray() self.bbox = self.getBoundingBox() self.xlen = self.bbox.max().x - self.bbox.min().x self.ylen = self.bbox.max().y - self.bbox.min().y self.zlen = self.bbox.max().z - self.bbox.min().z minEdge = self.xlen if minEdge > self.ylen: minEdge = self.ylen if minEdge > self.zlen: minEdge = self.zlen self.step = (minEdge - 2 * minEdge * self.borderOffset) / (self.density - 1.0) def decimalRange(self, start, end, step): while start < end: yield start start += step def pointToMObject(self, name): selectionList= om.MSelectionList() _mobject = om.MObject() selectionList.add(name) selectionList.getDependNode(0, _mobject) return _mobject def generateVoxel(self): outerPoint = om.MFloatPoint(self.bbox.max().x * 2, self.bbox.max().y * 2, self.bbox.max().z * 2) mfnMesh = om.MFnMesh(self.meshDagPath) transformationMatrix = om.MTransformationMatrix(self.mexclusive) rotation = transformationMatrix.rotation() rayDirection = om.MFloatVector(outerPoint) faceIds = None triIds = None idsSorted = False space = om.MSpace.kWorld maxParam = 999999 testBothDirections = False accelParams = None sortHits = True hitPoints = om.MFloatPointArray() hitRayParams = om.MFloatArray() hitFaces = om.MIntArray() voxelElement_mfnTransform = om.MFnTransform() dagModifier = om.MDagModifier() for x in self.decimalRange((self.bbox.min().x + self.xlen * self.borderOffset), self.bbox.max().x, self.step): for y in self.decimalRange((self.bbox.min().y + self.ylen * self.borderOffset), self.bbox.max().y, self.step): for z in self.decimalRange((self.bbox.min().z + self.zlen * self.borderOffset), self.bbox.max().z, self.step): point = om.MPoint(x, y, z) if self.typeIndex == VoxelGenerator.Local: point = self.localToWorldSpace(point) fPoint = om.MFloatPoint(point.x, point.y, point.z) hitPoints.clear() mfnMesh.allIntersections(fPoint, rayDirection, faceIds, triIds, idsSorted, space, maxParam, testBothDirections, accelParams, sortHits, hitPoints, hitRayParams, hitFaces, None, None, None, 0.000001) if hitPoints.length() % 2 == 0: continue voxelElement = dagModifier.createNode('locator') # pcube = cmds.polyCube(w=1, h=1, d=1, sx=1, sy=1, sz=1) # voxelElement = self.pointToMObject(pcube[0]) voxelElementMDagPath = om.MDagPath.getAPathTo(voxelElement) self.voxelObjects.append(voxelElement) voxelElement_mfnTransform.setObject(voxelElementMDagPath) voxelElement_mfnTransform.setTranslation(om.MVector(fPoint.x, fPoint.y, fPoint.z), om.MSpace.kWorld) if self.typeIndex == VoxelGenerator.Local: voxelElement_mfnTransform.setRotation(rotation) dagModifier.doIt() voxelCount = self.voxelObjects.length() return voxelCount def localToWorldSpace(self, point): mInclusive = self.meshDagPath.inclusiveMatrix() point = point * mInclusive return point def deleteVoxel(self): try: for i in range(self.voxelObjects.length()): om.MGlobal.deleteNode(self.voxelObjects[i]) except Exception as e: print e self.voxelObjects.clear() def getBoundingBox(self): meshDagNode = om.MFnDagNode(self.meshDagPath) bbox = meshDagNode.boundingBox() if self.typeIndex == VoxelGenerator.World: bbox.transformUsing(self.mexclusive) return bbox def voxelizerCreator(): return omp.asMPxPtr(Voxelizer()) def voxelizerInitializer(): # enum enumAttr = om.MFnEnumAttribute() Voxelizer.voxelType = enumAttr.create('voxelType', 'type') enumAttr.addField('World', 0) enumAttr.addField('Local', 1) # typeattr typedAttr = om.MFnTypedAttribute() Voxelizer.inputMesh = typedAttr.create('inputMesh', 'iMesh', om.MFnData.kMesh) typedAttr.disconnectBehavior = om.MFnAttribute.kReset # output numAttr = om.MFnNumericAttribute() Voxelizer.output = numAttr.create('output', 'out', om.MFnNumericData.kInt) numAttr.isReadable = True numAttr.isStorable = False # density density = om.MFnNumericAttribute() Voxelizer.voxelDensity = density.create('density', 'den', om.MFnNumericData.kShort) density.setDefault(5) density.setMin(2) Voxelizer.addAttribute(Voxelizer.voxelType) Voxelizer.addAttribute(Voxelizer.inputMesh) Voxelizer.addAttribute(Voxelizer.output) Voxelizer.addAttribute(Voxelizer.voxelDensity) Voxelizer.attributeAffects(Voxelizer.voxelType, Voxelizer.output) Voxelizer.attributeAffects(Voxelizer.voxelDensity, Voxelizer.output) Voxelizer.attributeAffects(Voxelizer.inputMesh, Voxelizer.output) def initializePlugin(mobject): mplugin = omp.MFnPlugin(mobject, 'lulongfei', '1.0', 'Any') try: mplugin.registerNode(kPluginNodeTypeName, voxelizerNodeId, voxelizerCreator, voxelizerInitializer) except: sys.stderr.write('Failed to register node: %s' % kPluginNodeTypeName) raise def uninitializePlugin(mobject): mplugin = omp.MFnPlugin(mobject) try: mplugin.deregister(voxelizerNodeId) except: sys.stderr.write('Failed to register node: %s' % kPluginNodeTypeName) raise
测试代码:
import maya.cmds as cmds
voxel = cmds.createNode('voxelizer')
cmds.connectAttr('%s.worldMesh' % 'pTorusShape1', '%s.inputMesh' % voxel)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。