当前位置:   article > 正文

Voxelization——体素化模型_mesh voxelizer

mesh voxelizer

上周部门内部进行分享知识讲座时讲到一个词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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129

测试代码:

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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这里说明下,加了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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240

测试代码:

import maya.cmds as cmds

voxel = cmds.createNode('voxelizer')
cmds.connectAttr('%s.worldMesh' % 'pTorusShape1', '%s.inputMesh' % voxel)
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/418591
推荐阅读
相关标签
  

闽ICP备14008679号