Exemplo n.º 1
0
 def rotate(self, o):
     """
     Realization of <Node.rotate(..)>
     """
     # check if we need to perform rotation
     cos = self.edges[1][2]
     convex = self.edges[1][3]
     if convex and abs(cos) < zero2:
         return
     
     angle = acos(cos)
     
     bm = getBmesh(o)
     bmesh.ops.rotate(
         bm,
         cent = zeroVector,
         matrix = mathutils.Matrix.Rotation(
             angle-math.pi/2. if convex else 1.5*math.pi-angle,
             3,
             self.n
         ),
         verts = getVertsForVertexGroup(o, bm, o.vertex_groups[ self._edges[1][1] ].name)
     )
     setBmesh(o, bm)
     
     return angle
Exemplo n.º 2
0
 def end(self, app):
     terrain = app.terrain
     for layer in app.layers:
         if layer.obj:
             setBmesh(layer.obj, layer.bm)
     if app.singleObject and not app.layered:
         # finalize BMesh
         setBmesh(self.obj, self.bm)
     
     bpy.context.scene.update()
     # Go through <app.layers> once again after <bpy.context.scene.update()>
     # to get correct results for <layer.obj.bound_box>
     for layer in app.layers:
         if layer.obj:
             if terrain and layer.sliceMesh:
                 self.slice(layer.obj, terrain, app)
             if layer.swModifier:
                 if not terrain.envelope:
                     terrain.createEnvelope()
                 self.addBoolenModifier(layer.obj, terrain.envelope)
                 self.addShrinkwrapModifier(layer.obj, terrain.terrain, layer.swOffset)
     
     bpy.ops.object.select_all(action="DESELECT")
     self.join()
     
     if terrain:
         terrain.cleanup()
Exemplo n.º 3
0
 def assignUv(self, uvMap=None):
     if not uvMap:
         uvMap = defaultUvMap
     o = self.obj
     # create a new UV map if necessary
     if not uvMap in o:
         o.data.uv_textures.new(uvMap)
     
     bm = getBmesh(o)
     # the inital loop
     _loop = self.getInitialLoop(bm)
     # find the open end if the finish sequence isn't a closed sequence
     loop = _loop
     vert = loop.vert
     while True:
         if len(vert.link_loops) == 1:
             # found the open end
             _loop = loop
             break
         loop = (vert.link_loops[1] if vert.link_loops[0] == loop else vert.link_loops[0]).link_loop_prev
         vert = loop.vert
         if loop == _loop:
             break
     
     # finally, assign UV coordinates
     layer = bm.loops.layers.uv[uvMap]
     # the layer for vertex groups
     groupLayer = bm.verts.layers.deform[0]
     h = getLevelHeight(self.context, o)
     offsetU = 0.
     loop = _loop
     vert = loop.vert
     e1 = getControlEmptyFromLoop(loop, groupLayer, o)
     while True:
         # remember the base loop
         _vert = vert
         # remember, we are dealing with rectangles
         # left bottom
         loop[layer].uv = (offsetU, 0.)
         # left top
         loop.link_loop_prev[layer].uv = (offsetU, h)
         # right bottom
         loop = loop.link_loop_next
         e2 = getControlEmptyFromLoop(loop, groupLayer, o)
         offsetU += (e2.location - e1.location).length
         loop[layer].uv = (offsetU, 0.)
         # right top
         loop.link_loop_next[layer].uv = (offsetU, h)
         
         # the step below isn't necessary, we already came to the required loop
         #loop = loop.link_loop_next
         vert = loop.vert
         if len(vert.link_loops) == 1:
             # reached the opposite end (end if the finish sequence isn't a closed sequence)
             break
         loop = vert.link_loops[1] if vert.link_loops[0] == loop else vert.link_loops[0]
         if loop == _loop:
             break
         e1 = e2
     setBmesh(o, bm)
Exemplo n.º 4
0
 def slice(obj, terrain, app):
     sliceSize = app.sliceSize
     bm = getBmesh(obj)
     
     def _slice(index, plane_no, terrainMin, terrainMax):
         # min and max value along the axis defined by <index>
         # 1) terrain
         # a simple conversion from the world coordinate system to the local one
         terrainMin = terrainMin - obj.location[index]
         terrainMax = terrainMax - obj.location[index]
         # 2) <bm>, i.e. Blender mesh
         minValue = min(obj.bound_box, key = lambda v: v[index])[index]
         maxValue = max(obj.bound_box, key = lambda v: v[index])[index]
         
         # cut everything off outside the terrain bounding box
         if minValue < terrainMin:
             minValue = terrainMin
             bmesh.ops.bisect_plane(
                 bm,
                 geom=bm.verts[:]+bm.edges[:]+bm.faces[:],
                 plane_co=(0., minValue, 0.) if index else (minValue, 0., 0.),
                 plane_no=plane_no,
                 clear_inner=True
             )
         
         if maxValue > terrainMax:
             maxValue = terrainMax
             bmesh.ops.bisect_plane(
                 bm,
                 geom=bm.verts[:]+bm.edges[:]+bm.faces[:],
                 plane_co=(0., maxValue, 0.) if index else (maxValue, 0., 0.),
                 plane_no=plane_no,
                 clear_outer=True
             )
         
         # now cut the slices
         width = maxValue - minValue
         if width > sliceSize:
             numSlices = math.ceil(width/sliceSize)
             _sliceSize = width/numSlices
             coord = minValue
             sliceIndex = 1
             while sliceIndex < numSlices:
                 coord += _sliceSize
                 bmesh.ops.bisect_plane(
                     bm,
                     geom=bm.verts[:]+bm.edges[:]+bm.faces[:],
                     plane_co=(0., coord, 0.) if index else (coord, 0., 0.),
                     plane_no=plane_no
                 )
                 sliceIndex += 1
     
     _slice(0, (1., 0., 0.), terrain.minX, terrain.maxX)
     _slice(1, (0., 1., 0.), terrain.minY, terrain.maxY)
     setBmesh(obj, bm)
Exemplo n.º 5
0
    def createFromArea(self, area):
        context = self.context
        controls = area.getControls()

        obj = createMeshObject(area.obj.name + "_finish")
        obj["t"] = self.type
        self.obj = obj

        bm = getBmesh(obj)
        # vertex groups are in the deform layer, create one before any operation with bmesh:
        layer = bm.verts.layers.deform.new()

        # a vector along z-axis with the length equal to the wall height
        height = getLevelHeight(context, area.obj) * zAxis
        numControls = len(controls)
        for c in controls:
            group = c["g"]
            # the vert at the bottom
            v_b = bm.verts.new(c.location)
            # the vert at the top
            v_t = bm.verts.new(c.location + height)
            assignGroupToVerts(obj, layer, group, v_b, v_t)
            # assign vertex group for the top vertex
            assignGroupToVerts(obj, layer, "t", v_t)

        # create faces
        bm.verts.ensure_lookup_table()
        v1_b = bm.verts[-2]
        v1_t = bm.verts[-1]
        for i in range(numControls):
            # <2> is the number of vertices (of the just created wall surface) per control point
            v2_b = bm.verts[i * 2]
            v2_t = bm.verts[i * 2 + 1]
            bm.faces.new((v1_b, v1_t, v2_t, v2_b))
            v1_b = v2_b
            v1_t = v2_t
        setBmesh(obj, bm)

        # without scene.update() hook modifiers will not work correctly
        context.scene.update()
        # perform parenting
        parent_set(area.obj.parent, obj)
        # one more update
        context.scene.update()

        # add HOOK modifiers
        for c in controls:
            group = c["g"]
            addHookModifier(obj, group, c, group)
        # add a HOOK modifier controlling the top vertices
        addHookModifier(obj, "t", getNextLevelParent(context, obj), "t")
        # add a SOLIDIFY modifier
        addSolidifyModifier(obj, "solidify", thickness=0.001, offset=1.)
        self.treatInsertions(controls)
Exemplo n.º 6
0
 def createFromArea(self, area):
     context = self.context
     controls = area.getControls()
     
     obj = createMeshObject(area.obj.name+"_finish")
     obj["t"] = self.type
     self.obj = obj
     
     bm = getBmesh(obj)
     # vertex groups are in the deform layer, create one before any operation with bmesh:
     layer = bm.verts.layers.deform.new()
     
     # a vector along z-axis with the length equal to the wall height
     height = getLevelHeight(context, area.obj)*zAxis
     numControls = len(controls)
     for c in controls:
         group = c["g"]
         # the vert at the bottom
         v_b = bm.verts.new(c.location)
         # the vert at the top
         v_t = bm.verts.new(c.location+height)
         assignGroupToVerts(obj, layer, group, v_b, v_t)
         # assign vertex group for the top vertex
         assignGroupToVerts(obj, layer, "t", v_t)
     
     # create faces
     bm.verts.ensure_lookup_table()
     v1_b = bm.verts[-2]
     v1_t = bm.verts[-1]
     for i in range(numControls):
         # <2> is the number of vertices (of the just created wall surface) per control point
         v2_b = bm.verts[i*2]
         v2_t = bm.verts[i*2+1]
         bm.faces.new((v1_b, v1_t, v2_t, v2_b))
         v1_b = v2_b
         v1_t = v2_t
     setBmesh(obj, bm)
     
     # without scene.update() hook modifiers will not work correctly
     context.scene.update()
     # perform parenting
     parent_set(area.obj.parent, obj)
     # one more update
     context.scene.update()
     
     # add HOOK modifiers
     for c in controls:
         group = c["g"]
         addHookModifier(obj, group, c, group)
     # add a HOOK modifier controlling the top vertices
     addHookModifier(obj, "t", getNextLevelParent(context, obj), "t")
     # add a SOLIDIFY modifier
     addSolidifyModifier(obj, "solidify", thickness=0.001, offset=1.)
     self.treatInsertions(controls)
Exemplo n.º 7
0
 def assignUv(self, uvMap=None):
     if not uvMap:
         uvMap = defaultUvMap
     o = self.obj
     # create a new UV map if necessary
     if not uvMap in o:
         o.data.uv_textures.new(uvMap)
     
     bm = getBmesh(self.obj)
     bm.verts.ensure_lookup_table()
     layer = bm.loops.layers.uv[uvMap]
     
     # the initial loop
     origin = bm.verts[0]
     start = origin.link_loops[0]
     # vector along the x-Axis of the area coordinate system
     firstLoopVector = start.link_loop_next.vert.co - origin.co
     firstLoopVector.normalize()
     
     # Calculate the transformation matrix from the level coordinate system to the area coordinate system,
     # where the x-axis is oriented along the first loop of <bm>,
     # y-axis lies in the plane of the area,
     # z-axis is parallel to the z-axis of the level coordinate system
     # translationMatrix is inversed translation matrix from the origin of the global coordinate system to the shape origin
     translationMatrix = mathutils.Matrix.Translation(-origin.co)
     # now calculate the inversed rotation matrix
     # create a rotation matrix instance with default values (i.e. a unit matrix)
     rotationMatrix = mathutils.Matrix()
     # cosine and sine of the angle between <xAxis> and <firstLoopVector>
     cos = firstLoopVector[0]
     sin = firstLoopVector[1]
     # rotation matrix from <firstLoopVector> to <xAxis> with -<zAxis> as the rotation axis
     rotationMatrix[0][0:2] = cos, sin
     rotationMatrix[1][0:2] = -sin, cos
     # remember inversed(TRS) = inversed(S)*inversed(R)*inversed(T), so in our case:
     matrix = rotationMatrix*translationMatrix
     
     loop = start
     while True:
         loop[layer].uv = (matrix*loop.vert.co)[:2]
         loop = loop.link_loop_next
         if loop == start:
             break
     setBmesh(o, bm)
Exemplo n.º 8
0
    def rotate(self, o):
        """
        Realization of <Node.rotate(..)>
        """
        # check if we need to perform rotation
        cos = self.edges[1][2]
        convex = self.edges[1][3]
        if convex and abs(cos) < zero2:
            return

        angle = acos(cos)

        bm = getBmesh(o)
        bmesh.ops.rotate(
            bm,
            cent=zeroVector,
            matrix=mathutils.Matrix.Rotation(
                angle - math.pi / 2. if convex else 1.5 * math.pi - angle, 3,
                self.n),
            verts=getVertsForVertexGroup(
                o, bm, o.vertex_groups[self._edges[1][1]].name))
        setBmesh(o, bm)

        return angle
Exemplo n.º 9
0
 def make(self, t, **kwargs):
     verts = t.bm.verts
     context = self.context
     hooksForNodes = kwargs["hooksForNodes"]
     # template Blender object
     _o = t.o
     # Create a Blender EMPTY object to serve as a parent for the window mesh;
     # its name doesn't have <T_> prefix
     name = _o.name[2:]
     # <pt> stands for parent template
     pt = t.parentTemplate
     if pt:
         p = createEmptyObject(name, _o.location-pt.o.location, False, empty_draw_size=0.01)
     else:
         # parent for the whole hierarchy of window Blender objects
         p = t.p
     t.meshParent = p
     # start a Blender object for the template
     o = createMeshObject(name + "_mesh")
     t.meshObject = o
     
     context.scene.update()
     # perform parenting
     parent_set(p, o)
     if t.parentTemplate:
         parent_set(pt.meshParent, p)
     context.scene.update()
     context.scene.objects.active = o
     
     t.prepareOffsets()
     
     # iterate through the vertices of the template Blender object
     numVerts = 0
     for v in verts:
         # id of the vertex
         vid = t.getVid(v)
         if not (vid in _o and _o[vid] in bpy.data.objects):
             continue
         # Blender object for the node at the vertex
         j = bpy.data.objects[_o[vid]]
         t.setNode(v, j, o, context, hooksForNodes = hooksForNodes)
         numVerts += 1
     
     # final operations: bridging or extruding edges loops of the nodes, making surfaces
     bm = getBmesh(o)
     t.bridgeOrExtendNodes(o, bm, kwargs["dissolveEndEdges"])
     if numVerts == len(verts):
         t.makeSurfaces(o, bm)
     setBmesh(o, bm)
     
     # remove unneeded vertex group
     groups = [g for g in o.vertex_groups if g.name[0] in ("e", "s", "c")]
     for g in groups:
         o.vertex_groups.remove(g)
     
     # add Edge Split modifier
     if kwargs["addEdgeSplitModifier"]:
         addEdgeSplitModifier(o, o.name)
         
     # set maximum possible range for the shape keys
     if hooksForNodes and o.data.shape_keys:
         for kb in o.data.shape_keys.key_blocks:
             kb.slider_min = -10.
             kb.slider_max = 10.
     
     t.insertAssets(context)
     
     # hide the template Blender object and all its children
     hide(t.o, True)
Exemplo n.º 10
0
    def assignUv(self, uvMap=None):
        if not uvMap:
            uvMap = defaultUvMap
        o = self.obj
        # create a new UV map if necessary
        if not uvMap in o:
            o.data.uv_textures.new(uvMap)

        bm = getBmesh(o)
        # the inital loop
        _loop = self.getInitialLoop(bm)
        # find the open end if the finish sequence isn't a closed sequence
        loop = _loop
        vert = loop.vert
        while True:
            if len(vert.link_loops) == 1:
                # found the open end
                _loop = loop
                break
            loop = (vert.link_loops[1] if vert.link_loops[0] == loop else
                    vert.link_loops[0]).link_loop_prev
            vert = loop.vert
            if loop == _loop:
                break

        # finally, assign UV coordinates
        layer = bm.loops.layers.uv[uvMap]
        # the layer for vertex groups
        groupLayer = bm.verts.layers.deform[0]
        h = getLevelHeight(self.context, o)
        offsetU = 0.
        loop = _loop
        vert = loop.vert
        e1 = getControlEmptyFromLoop(loop, groupLayer, o)
        while True:
            # remember the base loop
            _vert = vert
            # remember, we are dealing with rectangles
            # left bottom
            loop[layer].uv = (offsetU, 0.)
            # left top
            loop.link_loop_prev[layer].uv = (offsetU, h)
            # right bottom
            loop = loop.link_loop_next
            e2 = getControlEmptyFromLoop(loop, groupLayer, o)
            offsetU += (e2.location - e1.location).length
            loop[layer].uv = (offsetU, 0.)
            # right top
            loop.link_loop_next[layer].uv = (offsetU, h)

            # the step below isn't necessary, we already came to the required loop
            #loop = loop.link_loop_next
            vert = loop.vert
            if len(vert.link_loops) == 1:
                # reached the opposite end (end if the finish sequence isn't a closed sequence)
                break
            loop = vert.link_loops[1] if vert.link_loops[
                0] == loop else vert.link_loops[0]
            if loop == _loop:
                break
            e1 = e2
        setBmesh(o, bm)
Exemplo n.º 11
0
    def shear(self, o, angle):
        """
        Realization of <Node.shear(..)>
        """
        if angle is None or not "c" in o.vertex_groups:
            return

        _edges = self._edges

        convex = self.edges[1][3]
        bm = getBmesh(o)
        shearFactor = 1. / math.tan(angle / 2.)
        if convex:
            shearFactor = shearFactor - 1.
        else:
            shearFactor = -shearFactor - 1.

        # For the share transformation of the central part defined by the vertex group <c>
        # we may have to provide a space matrix, since the parameters of the share transformation are
        # defined under assumtions that the central part defined by the vertex group <c> is oriented
        # along the <baseBisector>. So the <space> matrix defines the rotation that orients
        # the actual bisector along the <baseBisector>
        bisector = (_edges[0][0] + _edges[1][0]).normalized()
        dot = bisector.dot(baseBisector)
        # check if <bisector> and <baseBisector> are already aligned
        if abs(1 - dot) > zero2:
            _angle = acos(dot)
            if self.n.dot(bisector.cross(baseBisector)) < 0.:
                _angle = -_angle
            spaceMatrix = mathutils.Matrix.Rotation(_angle, 4, self.n)
        else:
            spaceMatrix = mathutils.Matrix.Identity(4)

        bmesh.ops.transform(bm,
                            matrix=mathutils.Matrix.Shear(
                                'XY', 4, (shearFactor, 0.)),
                            verts=getVertsForVertexGroup(o, bm, "c"),
                            space=spaceMatrix)
        # Set BMesh here to get the correct result in the next section
        # of the code related to the shape key update
        setBmesh(o, bm)

        # update shape key data (if available) for the vertices of the vertex group <c>
        shape_keys = o.data.shape_keys
        if shape_keys:
            if shape_keys.key_blocks.get("frame_width", None):
                bm = getBmesh(o)

                shapeKey = bm.verts.layers.shape.get("frame_width")
                # the bisector of the edges after the shear transformation
                bisector = mathutils.Matrix.Rotation(
                    angle / 2. - math.pi / 4. if convex else 0.75 * math.pi -
                    angle / 2., 3, self.n) * bisector
                # offset vector for the shape key
                offset = shapeKeyOffset / math.sin(angle / 2.) * bisector
                for v in getVertsForVertexGroup(o, bm, "c"):
                    # check if the vertex changes its location for the shape key
                    if ((v[shapeKey] - v.co).length > zero2):
                        v[shapeKey] = v.co + offset

                setBmesh(o, bm)
Exemplo n.º 12
0
 def shear(self, o, angle):
     """
     Realization of <Node.shear(..)>
     """
     if angle is None or not "c" in o.vertex_groups:
         return
     
     _edges = self._edges
     
     convex = self.edges[1][3]
     bm = getBmesh(o)
     shearFactor = 1./math.tan(angle/2.)
     if convex:
         shearFactor = shearFactor - 1.
     else:
         shearFactor = -shearFactor - 1.
     
     # For the share transformation of the central part defined by the vertex group <c>
     # we may have to provide a space matrix, since the parameters of the share transformation are
     # defined under assumtions that the central part defined by the vertex group <c> is oriented
     # along the <baseBisector>. So the <space> matrix defines the rotation that orients
     # the actual bisector along the <baseBisector>
     bisector = (_edges[0][0] + _edges[1][0]).normalized()
     dot = bisector.dot(baseBisector)
     # check if <bisector> and <baseBisector> are already aligned
     if abs(1-dot) > zero2:
         _angle = acos(dot)
         if self.n.dot( bisector.cross(baseBisector) ) < 0.:
             _angle = -_angle
         spaceMatrix = mathutils.Matrix.Rotation(_angle, 4, self.n)
     else:
         spaceMatrix = mathutils.Matrix.Identity(4)
     
     bmesh.ops.transform(
         bm,
         matrix = mathutils.Matrix.Shear('XY', 4, (shearFactor, 0.)),
         verts = getVertsForVertexGroup(o, bm, "c"),
         space = spaceMatrix
     )
     # Set BMesh here to get the correct result in the next section
     # of the code related to the shape key update
     setBmesh(o, bm)
     
     # update shape key data (if available) for the vertices of the vertex group <c>
     shape_keys = o.data.shape_keys
     if shape_keys:
         if shape_keys.key_blocks.get("frame_width", None):
             bm = getBmesh(o)
             
             shapeKey = bm.verts.layers.shape.get("frame_width")
             # the bisector of the edges after the shear transformation
             bisector = mathutils.Matrix.Rotation(
                 angle/2. - math.pi/4. if convex else 0.75*math.pi - angle/2.,
                 3,
                 self.n
             ) * bisector
             # offset vector for the shape key
             offset = shapeKeyOffset / math.sin(angle/2.) * bisector
             for v in getVertsForVertexGroup(o, bm, "c"):
                 # check if the vertex changes its location for the shape key
                 if ( (v[shapeKey] - v.co).length > zero2):
                     v[shapeKey] = v.co + offset
             
             setBmesh(o, bm)
Exemplo n.º 13
0
    def make(self, t, **kwargs):
        verts = t.bm.verts
        context = self.context
        hooksForNodes = kwargs["hooksForNodes"]
        # template Blender object
        _o = t.o
        # Create a Blender EMPTY object to serve as a parent for the window mesh;
        # its name doesn't have <T_> prefix
        name = _o.name[2:]
        # <pt> stands for parent template
        pt = t.parentTemplate
        if pt:
            p = createEmptyObject(name,
                                  _o.location - pt.o.location,
                                  False,
                                  empty_draw_size=0.01)
        else:
            # parent for the whole hierarchy of window Blender objects
            p = t.p
        t.meshParent = p
        # start a Blender object for the template
        o = createMeshObject(name + "_mesh")
        t.meshObject = o

        context.scene.update()
        # perform parenting
        parent_set(p, o)
        if t.parentTemplate:
            parent_set(pt.meshParent, p)
        context.scene.update()
        context.scene.objects.active = o

        t.prepareOffsets()

        # iterate through the vertices of the template Blender object
        numVerts = 0
        for v in verts:
            # id of the vertex
            vid = t.getVid(v)
            if not (vid in _o and _o[vid] in bpy.data.objects):
                continue
            # Blender object for the node at the vertex
            j = bpy.data.objects[_o[vid]]
            t.setNode(v, j, o, context, hooksForNodes=hooksForNodes)
            numVerts += 1

        # final operations: bridging or extruding edges loops of the nodes, making surfaces
        bm = getBmesh(o)
        t.bridgeOrExtendNodes(o, bm, kwargs["dissolveEndEdges"])
        if numVerts == len(verts):
            t.makeSurfaces(o, bm)
        setBmesh(o, bm)

        # remove unneeded vertex group
        groups = [g for g in o.vertex_groups if g.name[0] in ("e", "s", "c")]
        for g in groups:
            o.vertex_groups.remove(g)

        # add Edge Split modifier
        if kwargs["addEdgeSplitModifier"]:
            addEdgeSplitModifier(o, o.name)

        # set maximum possible range for the shape keys
        if hooksForNodes and o.data.shape_keys:
            for kb in o.data.shape_keys.key_blocks:
                kb.slider_min = -10.
                kb.slider_max = 10.

        t.insertAssets(context)

        # hide the template Blender object and all its children
        hide(t.o, True)
Exemplo n.º 14
0
    def createEnvelope(self):
        terrain = self.terrain
        name = "%s_envelope" % terrain.name
        if name in bpy.data.objects:
            # use existing Blender object
            envelope = bpy.data.objects[name]
            # delete modifiers
            for m in reversed(envelope.modifiers):
                envelope.modifiers.remove(m)
        else:
            # create new envelope for the terrain
            envelope = createMeshObject(name, (0., 0., self.minZ),
                                        terrain.data.copy())
            # flatten the terrain envelope
            envelope.scale[2] = 0.
            envelope.select = True
            bpy.context.scene.objects.active = envelope
            bpy.ops.object.transform_apply(location=False,
                                           rotation=False,
                                           scale=True)

            bpy.ops.object.mode_set(mode='EDIT')
            bpy.ops.mesh.select_all(action='DESELECT')
            bpy.ops.mesh.select_mode(type='FACE')
            bpy.ops.object.mode_set(mode='OBJECT')
            for p in envelope.data.polygons:
                if p.normal[2] < 0.:
                    p.select = True
            bpy.ops.object.mode_set(mode='EDIT')
            bpy.ops.mesh.delete(type='FACE')
            bpy.ops.mesh.select_mode(type='VERT')
            bpy.ops.mesh.select_all(action='SELECT')
            bpy.ops.mesh.remove_doubles()
            bpy.ops.mesh.region_to_loop()
            bpy.ops.mesh.select_all(action='INVERT')
            bpy.ops.mesh.dissolve_verts(use_face_split=False,
                                        use_boundary_tear=False)
            bpy.ops.mesh.select_all(action='SELECT')
            #bpy.ops.mesh.dissolve_limited(angle_limit=math.radians(0.1))
            bpy.ops.object.mode_set(mode='OBJECT')
            bm = getBmesh(envelope)
            for f in bm.faces:
                f.smooth = False
                # ensure all normals point upward
                pointNormalUpward(f)
            # inset faces to avoid weird results of the BOOLEAN modifier
            insetFaces = bmesh.ops.inset_region(bm,
                                                faces=bm.faces,
                                                use_boundary=True,
                                                use_even_offset=True,
                                                use_interpolate=True,
                                                use_relative_offset=False,
                                                use_edge_rail=False,
                                                use_outset=False,
                                                thickness=self.envelopeInset,
                                                depth=0.)['faces']
            bmesh.ops.delete(bm, geom=insetFaces, context=5)
            setBmesh(envelope, bm)

        self.envelope = envelope
        envelope.select = False
        envelope.hide_render = True
        # hide <envelope> after all Blender operator
        envelope.hide = True
        # SOLIDIFY modifier instead of BMesh extrude operator
        m = envelope.modifiers.new(name="Solidify", type='SOLIDIFY')
        m.offset = 1.
        m.thickness = self.maxZ - self.minZ + self.envelopeOffset