Hi All,
Here is another addon I wrote to use BMesh. This allows you to extrude a mesh face along a bezier spline with options to rotate and scale the extrusion as well as change the number of extrusion steps.
To use the script either paste the code into the text editor and run script, or save as a .py and install as an addon. Then follow these steps;
EDIT
- Ensure you have a bezier curve object in your scene
- Select the face to extrude in the mesh in edit mode.
- In the spacebar menu search for āExtrude Along Curveā and select āExtrude Along Curveā.
- Set the operator properties as you require, if your curve object contains more than one spline, you can select which using the Spline Index property.
Hereās an example using just a cube and a single bezier curve.
You can download the Py script at the link below:
Download Py
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
bl_info = {
"name": "Extrude Along Curve",
"author": "Andrew Hale (TrumanBlending)",
"version": (0, 1),
"blender": (2, 6, 3),
"location": "",
"description": "Extrude a face along a Bezier Curve",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"}
import bpy
import bmesh
from mathutils import Vector, Quaternion
from math import ceil, floor, pi
def eval_bez_tan(mat, points, t):
num = len(points)
t *= num - 1
upper = ceil(t)
lower = floor(t)
if upper == lower:
if upper == 0:
return (mat * (points[upper].handle_right - points[upper].co)).normalized()
elif upper == num - 1:
return (mat * (points[upper].co - points[upper].handle_left)).normalized()
else:
return (mat * (points[upper].co - points[upper].handle_left)).normalized()
else:
t -= lower
pupper = points[upper]
plower = points[lower]
tangent = -3 * (1 - t) ** 2 * plower.co + (-6 * (1 - t) * t + 3 * (1 - t) ** 2) * plower.handle_right + (-3 * t ** 2 + 3 * (1 - t) * 2 * t) * pupper.handle_left + 3 * t ** 2 * pupper.co
tangent = mat * tangent
tangent.normalize()
return tangent
def eval_bez(mat, points, t):
num = len(points)
t *= num - 1
upper = ceil(t)
lower = floor(t)
if upper == lower:
return mat * points[upper].co
else:
t -= lower
pupper = points[upper]
plower = points[lower]
pos = (1 - t) ** 3 * plower.co + 3 * (1 - t) ** 2 * t * plower.handle_right + 3 * (1 - t) * t ** 2 * pupper.handle_left + t ** 3 * pupper.co
return mat * pos
def curve_ob_enum(self, context):
obs = context.scene.objects
cuobs = [(str(i), ob.name, ob.name) for i, ob in enumerate(obs) if ob.type == 'CURVE']
curve_ob_enum.temp = cuobs
return cuobs
class ExtrudeAlongCurve(bpy.types.Operator):
bl_idname = "mesh.extrude_along_curve"
bl_label = "Extrude Along Curve"
bl_options = {'REGISTER', 'UNDO'}
resolution = bpy.props.IntProperty(name="Resolution", default=1, min=1, soft_max=100)
scale = bpy.props.FloatProperty(name="Scale", default=1.0, soft_min=0.0, soft_max=5.0)
rotation = bpy.props.FloatProperty(name="Rotation", default=0.0, soft_min=-2 * pi, soft_max=2 * pi, subtype='ANGLE')
splineidx = bpy.props.IntProperty(name="Spline Index", default=0, min=0)
snapto = bpy.props.BoolProperty(name="Snap To Face", default=True)
curveob = bpy.props.EnumProperty(name="Curve", items=curve_ob_enum)
@classmethod
def poll(self, context):
ob = context.active_object
for cuob in context.scene.objects:
if cuob.type == 'CURVE':
break
else:
return False
return (ob is not None) and (ob.type == 'MESH') and (context.mode == 'EDIT_MESH')
def draw(self, context):
layout = self.layout
layout.prop(self, "curveob", text="", icon='CURVE_DATA')
layout.prop(self, "resolution")
layout.prop(self, "scale")
layout.prop(self, "rotation")
layout.prop(self, "splineidx")
layout.prop(self, "snapto")
def execute(self, context):
ob = bpy.context.active_object
me = ob.data
bm = bmesh.from_edit_mesh(me)
# Get the selected curve object and the required spline
cuob = context.scene.objects[int(self.curveob)]
cu = cuob.data
self.splineidx = min(self.splineidx, len(cu.splines) - 1)
p = cu.splines[self.splineidx].bezier_points
# Get the property values
res = self.resolution
scale = self.scale
rotation = self.rotation
dscale = (1 - scale) / res
drot = rotation / res
# Get the matrices to convert between spaces
cmat = ob.matrix_world.inverted() * cuob.matrix_world
ctanmat = cmat.to_3x3().inverted().transposed()
# The list of parameter values to evaluate the bezier curve at
tvals = [t / res for t in range(res + 1)]
# Get the first selected face, if none, cancel
for f in bm.faces:
if f.select:
break
else:
return {'CANCELLED'}
# Get the position vecs on the curve and tangent values
bezval = [eval_bez(cmat, p, t) for t in tvals]
beztan = [eval_bez_tan(ctanmat, p, t) for t in tvals]
bezquat = [0] * len(tvals)
# Using curve only
bezquat[0] = beztan[0].to_track_quat('Z', 'Y')
fquat = bezquat[0].inverted()
# Calculate the min twist orientations
for i in range(1, res + 1):
ang = beztan[i - 1].angle(beztan[i], 0.0)
if ang > 0.0:
axis = beztan[i - 1].cross(beztan[i])
q = Quaternion(axis, ang)
bezquat[i] = q * bezquat[i - 1]
else:
bezquat[i] = bezquat[i - 1].copy()
# Get the faces to be modified
fprev = f
no = f.normal.copy()
faces = [f.copy() for i in range(res)]
# Offset if we need to snap to the face
offset = Vector() if not self.snapto else (f.calc_center_median() - bezval[0])
# For each of the faces created, set their vert positions and create side faces
for i, data in enumerate(zip(faces, bezval[1:], bezquat[1:])):
fn, pos, quat = data
cen = fn.calc_center_median()
rotquat = Quaternion((0, 0, 1), i * drot)
for v in fn.verts:
v.co = quat * rotquat * fquat * (v.co - cen) * (1 - (i + 1) * dscale) + pos + offset
for ll, ul in zip(fprev.loops, fn.loops):
ff = bm.faces.new((ll.vert, ll.link_loop_next.vert, ul.link_loop_next.vert, ul.vert))
ff.normal_update()
bm.faces.remove(fprev)
fprev = fn
me.calc_tessface()
me.calc_normals()
me.update()
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()
Hope this script comes in useful.
Cheers,
Truman