Make a variety of sword, knives, hammer/axes, polearms, and much more with only a few lines code.

The code is split into 3 libraries:
Mesh.py: Tools for processing triangle meshes. The triangle mesh is a list of triangles composed of 3 vertices, each with a corresponding colour (RGB from 0 to 1).
Currently the library only interprets the triangle meshes into a vpython render.
Shortly I will add .obj export here as well, but I’m neglecting to do so until I write a nice way to compress the recurring vertices into a single element while preserving the per vertex colouring as a wavefront texture.

Handle.py: is a general extruder and can be used for more than handles. It’s simlar to the vpython extrusion function but returns a triangle mesh with per vertex colouring instead of a vpython render.

and,
Blade.py: The blade sculptor. Requiring a height, width, and thickness function converting the x,y position into a thickness.
The thickness function is used for crafting the blade edges as well the rate the blade thickens at the base/centre.
The blade requires both a left and right shape, for sculpting the curves on each side of the blade.
Contains both a version for vpython and one returning a triangle mesh.

Mesh.py

from vpython import vertex,vector,triangle
import vpython

def draw_mesh(mesh):
    for i in mesh:
        triangle(v0=vertex(pos=vector(*i[0][0]),color=vector(*i[0][1]))
                 ,v1=vertex(pos=vector(*i[1][0]),color=vector(*i[1][1])),
                 v2=vertex(pos=vector(*i[2][0]),color=vector(*i[2][1])))

Handle.py

from meshes import draw_mesh
from vpython import vertex,vector,triangle,textures,bumpmaps,faces
import vpython
from math import cos,sin,pi,atan2
from random import random
from blade import blade_mesh
def path_roll(path,roll):
    while len(roll)<len(path):
        roll.append(0)
    return list(map(lambda a,b:(a[0],a[1],b),path,roll))

def plane(origin,xaxis,yaxis,roll=0,scale=1):
    def outfunc(x,y):
        rollx=cos(roll)*x+cos(roll+pi/2)*y
        rolly=sin(roll)*x+sin(roll+pi/2)*y
        xmove=[i*rollx*scale for i in xaxis]
        ymove=[i*rolly*scale for i in yaxis]
        return tuple(map(lambda a,b,c:a+b+c,origin,xmove,ymove))
        #return origin[0]+xmove[0]+ymove[0],origin[1]+xmove[1]+ymove[1],origin[2]+xmove[2]+ymove[2]
    return outfunc

testplane=plane((2,2,2),(1,0,0),(0,1,0),pi/2)

def midangle(a,b):
    return (a+b)/2
    
def path_roll_to_planes(pathroll,closed=False,startangle=None,endangle=None,offset=(0,0,0)):
    angles=[]
    planes=[]
    if closed:
        pathroll.append(pathroll[-1])
    for i in range(len(pathroll)-1):
        p1=pathroll[i]
        p2=pathroll[i+1]
        angles.append(atan2(p2[1]-p1[1],p2[0]-p1[0])+pi/2)
    if closed:
        pass
    else:
        if startangle==None:
            start=angles[0]
        else:
            start=startangle
        if endangle==None:
            end=angles[-1]
        else:
            end=endangle
        mid=[]
        for i in range(len(angles)-1):
            mid.append(midangle(angles[i],angles[i+1]))
        angles=[start]+mid+[end]
        for i in range(len(angles)):
            #make plane
            #Path on X & Z, shape on XZ hypotenuse and Y
            #origin=(offset[0]+pathroll[i][0],offset[1],offset[2]+pathroll[i][2])
            #xaxis=(cos(angles[i]),0,sin(angles[i]))
            #yaxis=(0,1,0)
            #Path on Y&Z, shape on YZ hypotenuse and X
            origin=(offset[0],pathroll[i][0]+offset[1],pathroll[i][1]+offset[2])
            xaxis=(0,cos(angles[i]),sin(angles[i]))
            yaxis=(1,0,0)
            planes.append(plane(origin,xaxis,yaxis,pathroll[i][2]))
    return planes

def randcol(basecol,variation=.5):
    v=(random()-.5)*variation
    return (basecol[0]+v,basecol[1]+v,basecol[2]+v)

def extrusion2d(shape2d,path,closed=False,col=(.5,.5,.5),origin=(0,0,0)):
    triangle_mesh=[]
    planes=path_roll_to_planes(path,closed,offset=origin)
    points=[[i(*j) for j in shape2d] for i in planes]
    for i in range(len(points)-1):
        for j in range(len(shape2d)-1):
            v0=(points[i+1][j],randcol(col,.2))
            v1=(points[i+1][j+1],randcol(col,.2))
            v2=(points[i][j+1],randcol(col,.2))
            v3=(points[i][j],randcol(col,.2))
            triangle_mesh.append((v0,v1,v2))
            triangle_mesh.append((v0,v2,v3))
    bottomcenter=(planes[0](0,0),randcol(col,.2))
    topcenter=(planes[-1](0,0),randcol(col,.2))
    for j in range(len(shape2d)-1):
        #v0=(points[0][j],randcol(col,.2))
        #v1=(points[0][j+1],randcol(col,.2))
        #triangle_mesh.append((v0,v1,v2))
        v0=(points[0][j],randcol(col,.2))
        v1=(points[0][j+1],randcol(col,.2))
        triangle_mesh.append((v0,v1,bottomcenter))
        v0=(points[-1][j],randcol(col,.2))
        v1=(points[-1][j+1],randcol(col,.2))
        triangle_mesh.append((v0,v1,topcenter))
    return triangle_mesh

Blade.py

from meshes import draw_mesh
from vpython import vertex,vector,triangle,textures,bumpmaps
import vpython
from math import cos,sin,pi
from random import random
def blade(height,width,thicknessfunction=lambda xr,yr:((.5-xr*.5)**(.3+xr*.75))/8,basepos=(0,0,0),axis=(0,1,0),leftfunction=lambda x:0,rightfunction=lambda x:0):
    triangles=[]
    vsteps=64
    vstep=1/vsteps*height
    hsteps=36
    bottomrowheight=basepos[1]
    front=[]
    back=[]
    for i in range(vsteps+1):
        relativeheight=i/vsteps
        frontrow=[]
        backrow=[]
        left=(width*leftfunction(relativeheight))
        rowwidth=max(0,width-(width*rightfunction(relativeheight))-left)
        if rowwidth>0:
            for j in range(hsteps+1):
                relativewidth=j/hsteps
                x=left+basepos[0]+j/hsteps*rowwidth
                y=basepos[1]+i/vsteps*height
                z=thicknessfunction(relativewidth,relativeheight)
                rcol=random()*.3+.4
                point=vertex(pos=vector(z,y, x),color=vector(rcol,rcol,rcol))
                frontrow.append(point)
                rcol=random()*.3+.4
                point=vertex(pos=vector(-z,y,x),color=vector(rcol,rcol,rcol))
                backrow.append(point)
            front.append(frontrow)
            back.append(backrow)

    #front and back faces
    for i in range(vsteps):
        for j in range(hsteps):
            triangles.append(triangle(v0=front[i][j+1],v1=front[i+1][j+1],v2=front[i+1][j]))
            triangles.append(triangle(v0=front[i][j+1],v1=front[i+1][j],v2=front[i][j]))

            triangles.append(triangle(v0=back[i+1][j],v1=back[i+1][j+1],v2=back[i][j+1]))
            triangles.append(triangle(v0=back[i][j],v1=back[i+1][j],v2=back[i][j+1]))
    #seal ends
    for i in range(vsteps):
        triangles.append(triangle(v0=front[i][0],v1=back[i][0],v2=back[i+1][0]))
        triangles.append(triangle(v0=front[i][0],v1=back[i+1][0],v2=front[i+1][0]))
    triangles.append(triangle(v0=front[i][-1],v1=back[i][-1],v2=back[i+1][-1]))
    triangles.append(triangle(v0=front[i][-1],v1=back[i+1][-1],v2=front[i+1][-1]))        
    return front+back

def blade_mesh(height,width,thicknessfunction=lambda xr,yr:((.5-xr*.5)**(.3+xr*.75))/8,basepos=(0,0,0),axis=(0,1,0),leftfunction=lambda x:sin(x*3.14)*.1,rightfunction=lambda x:-sin(x*3.14)*.1,resolution=(32,32),roll=0):
    triangles=[]
    vsteps=resolution[1]
    vstep=1/vsteps*height
    hsteps=resolution[0]
    bottomrowheight=basepos[1]
    front=[]
    back=[]
    for i in range(vsteps+1):
        relativeheight=i/vsteps
        frontrow=[]
        backrow=[]
        left=(width*leftfunction(relativeheight))
        rowwidth=max(0,width-(width*rightfunction(relativeheight))-left)
        if rowwidth>0:
            for j in range(hsteps+1):
                relativewidth=j/hsteps
                x=left+basepos[0]+j/hsteps*rowwidth
                y=basepos[1]+i/vsteps*height
                z=thicknessfunction(relativewidth,relativeheight)
                rcol=random()*.3+.4
                point=((z,y,x),(rcol,rcol,rcol))
                frontrow.append(point)
                rcol=random()*.3+.4
                point=((-z,y,x),(rcol,rcol,rcol))
                backrow.append(point)
            front.append(frontrow)
            back.append(backrow)
    #front and back faces
    for i in range(vsteps):
        for j in range(hsteps):
            triangles.append((front[i][j+1],front[i+1][j+1],front[i+1][j]))
            triangles.append((front[i][j+1],front[i+1][j],front[i][j]))

            triangles.append((back[i+1][j],back[i+1][j+1],back[i][j+1]))
            triangles.append((back[i][j],back[i+1][j],back[i][j+1]))
    #seal ends
    for i in range(vsteps):
        triangles.append((front[i][0],back[i][0],back[i+1][0]))
        triangles.append((front[i][0],back[i+1][0],front[i+1][0]))
        triangles.append((front[i][-1],back[i][-1],back[i+1][-1]))
        triangles.append((front[i][-1],back[i+1][-1],front[i+1][-1]))
    for j in range(hsteps):
        triangles.append((front[0][j],back[0][j],back[0][j+1]))
        triangles.append((front[0][j],back[0][j+1],front[0][j+1]))
        triangles.append((front[-1][j],back[-1][j],back[-1][j+1]))
        triangles.append((front[-1][j],back[-1][j+1],front[-1][j+1]))
    return triangles

Demo.py

from handles import extrusion2d
from meshes import draw_mesh
from math import cos,sin,pi
from blade import blade_mesh

####2dshapes
polygon=lambda sides,r=1,center=(0,0):[(center[0]+cos(i/sides*pi*2)*r,center[1]+sin(i/sides*pi*2)*r) for i in range(sides+1)]
square=lambda n,center=(0,0):[(center[0]-n,center[1]-n),(center[0]+n,center[1]-n),(center[0]+n,center[1]+n),(center[0]-n,center[1]+n),(center[0]-n,center[1]-n)]

######weapon maker#####

###Cleaver
#handle=extrusion2d(polygon(8,.2),[(-2,0,0),(.5,0,0)],col=(.6,.5,.2),origin=(.25,0,0))
#cleaver=blade_mesh(3,1.5,thicknessfunction=lambda xr,yr:((.5-xr*.5)**(.3+xr*.75))/8,basepos=(0,0,0),axis=(0,1,0),leftfunction=lambda x:sin(x*3.14)*.1,rightfunction=lambda x:-sin(x*3.14)*.1)
#draw_mesh(handle)
#draw_mesh(cleaver)

###Spear
#handle=extrusion2d(polygon(8,.2),[(-4,0,0),(1,0,0)],col=(.6,.5,.2),origin=(.25,0,0))
#speartip=blade_mesh(3,.8,thicknessfunction=lambda xr,yr:(1-yr)*(.5-abs(.5-xr))*.5,basepos=(-.2,1,-.25),axis=(0,1,0),leftfunction=lambda x:x*.49,rightfunction=lambda x:x*.5)
#draw_mesh(handle)
#draw_mesh(speartip)

###Dagger 1
#handle=extrusion2d(polygon(8,.2),[(-1.4,0,0),(-.4,0,0)],col=(.6,.5,.2),origin=(.25,0,0))
#daggertip=blade_mesh(2,.8,thicknessfunction=lambda xr,yr:(1-yr)*(.5-abs(.5-xr))*.5,basepos=(-.2,-.4,-.25),axis=(0,1,0),leftfunction=lambda x:x*.49,rightfunction=lambda x:x*.5)
#draw_mesh(handle)
#draw_mesh(daggertip)

###sabre
#handletop=extrusion2d(polygon(8,.4),[(-.5,0,0),(-.1,0,0)],col=(.6,.5,.2),origin=(.25,0,0))
#handle=extrusion2d(polygon(8,.2),[(-1.4,0,0),(-1,0,0),(-.7,0,0),(-.4,0,0)],col=(.6,.5,.2),origin=(.25,0,0))
#blade=blade_mesh(3,.5,thicknessfunction=lambda xr,yr:max((1-yr)*.9+.1,1-xr)*.05*(1.5-yr),basepos=(0,-.4,0),axis=(0,1,0),leftfunction=lambda x:sin(x*pi*1.2-pi/4)*.35+.4,rightfunction=lambda x:(-1.6+x*2 if x >.8 else 0)+1.1*abs(x-.7)**2-sin(x*pi*1.2-pi/4)*.25-.4)
#draw_mesh(blade)
#draw_mesh(handle)
#draw_mesh(handletop)

###broad sword
#handletop=extrusion2d([(-.2,-.6),(-.2,.6),(.2,.6),(.2,-.6),(-.2,-.6)],[(-.5,0,0),(-.2,0,0)],col=(.6,.5,.2),origin=(.5,0,0))
#handle=extrusion2d(polygon(8,.2),[(-2.5,0,0),(-1.7,0,0),(-1.0,0,0),(-.5,0,0)],col=(.6,.5,.2),origin=(.5,0,0))
#pomel=extrusion2d(polygon(8,.3),[(-2.7,0,0),(-2.5,0,0)],col=(.6,.5,.2),origin=(.5,0,0))
#blade=blade_mesh(6,1,thicknessfunction=lambda xr,yr:max((1-yr)*.9+.1,abs(.5-xr))*.05*(1.5-yr),basepos=(0,-.4,0),axis=(0,1,0),leftfunction=lambda x:.45*x**3,rightfunction=lambda x:.45*x**3)
#draw_mesh(blade)
#draw_mesh(pomel)
#draw_mesh(handle)
#draw_mesh(handletop)

#dagger2
#handletop=extrusion2d([(-.15,-.4),(-.15,.4),(.15,.4),(.15,-.4),(-.15,-.4)],[(-.5,0,0),(-.2,0,0)],col=(.6,.5,.2),origin=(.2,0,0))
#handle=extrusion2d(polygon(8,.125),[(-1.2,0,0),(-1.0,0,0),(-.5,0,0)],col=(.6,.5,.2),origin=(.2,0,0))
#pomel=extrusion2d(polygon(8,.2),[(-1.5,0,0),(-1.2,0,0)],col=(.6,.5,.2),origin=(.2,0,0))
#blade=blade_mesh(3,.35,thicknessfunction=lambda xr,yr:max((1-yr)*.9+.1,abs(.5-xr))*.05*(1.5-yr),basepos=(0,-.4,0),axis=(0,1,0),leftfunction=lambda x:.45*x**3,rightfunction=lambda x:.45*x**3)
#draw_mesh(blade)
#draw_mesh(pomel)
#draw_mesh(handle)
#draw_mesh(handletop)


#dagger3
handletop=extrusion2d([(-.15,-.4),(-.15,.4),(.15,.4),(.15,-.4),(-.15,-.4)],[(-1.5,0,0),(-1.4,0,0)],col=(.6,.5,.2),origin=(.3,0,0))
handle=extrusion2d(polygon(8,.125),[(-2.2,0,0),(-2.0,0,0),(-1.5,0,0)],col=(.6,.5,.2),origin=(.3,0,0))
pomel=extrusion2d(polygon(8,.2),[(-2.5,0,0),(-2.2,0,0)],col=(.6,.5,.2),origin=(.3,0,0))
blade=blade_mesh(6,.6,thicknessfunction=lambda xr,yr:max((1-yr)*.9+.1,.5-abs(.5-xr))*.05*(1.5-yr),basepos=(0,-1.4,0),axis=(0,1,0),leftfunction=lambda x:.45*x**3,rightfunction=lambda x:.45*x**3)
draw_mesh(blade)
draw_mesh(pomel)
draw_mesh(handle)
draw_mesh(handletop)


#Saw
#sawblade=blade_mesh(16,3,thicknessfunction=lambda xr,yr:.05-xr*.05,basepos=(0,-8,0),axis=(0,1,0),leftfunction=lambda x:sin(x*3.14)*.2-abs(.3-x)*.4,rightfunction=lambda x:-sin(x*3.14)*.2+abs(sin(x*120))*.07,resolution=(10,256))
#handle1=extrusion2d(polygon(8,.4),[(0,-4,0),(0,-1.0,0),(0,2,0)],col=(.3,.2,.1),origin=(0,-7.5,0))
#handle2=extrusion2d(polygon(8,.4),[(0,-4,0),(0,-1.0,0),(0,2,0)],col=(.3,.2,.1),origin=(0,7.5,0))
#draw_mesh(handle1)
#draw_mesh(handle2)
#draw_mesh(sawblade)

#Tonfa
#handle1=extrusion2d(polygon(4,.4),[(0,-3,0),(0,-1.5,0),(0,0,0)],col=(.3,.2,.1),origin=(0,0,0))
#handle2=extrusion2d(polygon(8,.4),[(-3,0,0),(0,0,0),(7,0,0)],col=(.3,.2,.1),origin=(0,0,0))
#draw_mesh(handle1)
#draw_mesh(handle2)

##Polearm
blade=blade_mesh(4,1,thicknessfunction=lambda xr,yr:.05-xr*.05,basepos=(0,4,0),axis=(0,1,0),leftfunction=lambda x:0,rightfunction=lambda x:-sin((x+1)*3.14*.49)*1.4,resolution=(10,40))
handle1=extrusion2d(polygon(4,.2),[(0,0,0),(0,1.0,0),(0,3.0,0)],col=(.4,.1,.1),origin=(0,4,0))
handle2=extrusion2d(polygon(64,.3),[(-3,0,0),(0,0,0),(8,0,0)],col=(.4,.1,.1),origin=(0,0,0))
handle3=extrusion2d(polygon(64,.5),[(7.5,0,0),(8,0,0)],col=(.4,.1,.1),origin=(0,0,0))
draw_mesh(handle1)
draw_mesh(handle2)
draw_mesh(handle3)
draw_mesh(blade)

Back to top