User Tools

Site Tools


blender:main-blender-create-track

Back to Blender Help Design

Here we will build a track using the capability of Blender to create an object by sliding a profile, or a cross-section along a curve. So the first step is to give a few way-points to give our track it's basic shapes. As usual, we will use the python API of Blender as it allows a quick redesign of the track.

We start by transforming the polygon into a bezier circular curve. Each vertex of the polygon is defined by 5 values: x,y,z,turn0,turn1

    pts=[]
    pts.append([   0.0,   0.0,0.0,30.0,20.0])  # x,y,z,turn0,turn1
    pts.append([   0.0, 160.0,0.0,20.0,20.0])
    pts.append([ 100.0, 200.0,0.0,20.0,20.0])
    pts.append([ 180.0,   0.0,5.0,20.0,10.0])
    pts.append([-180.0, -50.0,5.0,10.0,10.0])
    pts.append([-180.0,-160.0,0.0,10.0,20.0])
    pts.append([   0.0,-200.0,0.0,10.0,30.0])

If we let the track as a polygon, the turn will be very difficult to achieve. So we reduce the size of the straight parts to let some room for the turns :

  • turn0 is the place left for the curve before reaching the vertex
  • turn1 is the place left for the curve after passing the vertex

So we replace the pts array by the coordinates x,y,z of the points at the beginning and at the end of the curves. As we will use a bezier, we also need to define the handle on the left (xhl,yhl,zhl) and on the right (xhr,yhr,zhr) of each point; this is done by the function (def_track_curve) :

x,y,z,xhl,yhl,zhl,xhr,yhr,zhr = def_track_curves(pts)

As the track is cyclic, we the use blender function primitive_bezier_circle_add() to have a closed bezier curve. For this curve, the number of support points is defined by ncut, the number of subdivisions and its value is :

npt_bez = 4*(ncut+1)

So npt_bez must be a multiple of 4. We decide also to have a smoother curve by multiplying by an oversampling factor the minimal number of points required to build the track. A loop is used to find the value of ncut with an oversampling factor of 4 :

    n0=len(pts)
    ncut = 1
    over_sample = 4 # define over sampling factor to have a smoother curve
    while True:
        npt_bez = 4*(ncut+1)  # compute the number of beziers points
        if npt_bez > over_sample*n0:
            break  # exit if subdivision is enough
        ncut += 1
    npt = npt_bez

The number of points in x,y,z is lower than npt. Some interpolated points are added between the existing points. First we define the number of points to put in each segment of the track :

    t_cnt = def_points_in_segments(npt,x,y)

Then we use a bezier function to compute the coordinates of these new points. The refined definition of the track with npt points is stored in lpts:

    lpts = []
    n = len(x)
    for i0 in range(n):
        i1 = (i0+1) % n
        new_pts = geometry.interpolate_bezier(
            (x[i0],y[i0],z[i0]),
            (xhr[i0],yhr[i0],zhr[i0]),
            (xhl[i1],yhl[i1],zhl[i1]),
            (x[i1],y[i1],z[i1]),
            t_cnt[i0]+1) # trick : compute one more points to avoid duplicate of the first
        for ipt in range(len(new_pts)-1):
            pt =new_pts[ipt+1]
            lpts.append([pt.x,pt.y,pt.z])

Once lpts is defined by def_track_1(), standard blender functions are used to build the track and add the gates. def_track_1() function also return n, the number of bezier points, and ncut the number of subdivisions in the circular bezier curver. ops.curve.primitive_bezier_circle_add() create a default circular bezier curver with 4 bezier points. Then we change mode to EDIT and subdivide the curve with ncut cuts. Then we go back to OBJECT mode.

# Get the track waypoints
ncut,n,lpts = def_track_1()
# Create a bezier circle and enter edit mode.
ops.curve.primitive_bezier_circle_add (radius=1.0,
                                      location=(0.0, 0.0, 0.0),
                                      enter_editmode=False)                                      
# Subdivide the curve by a number of cuts
ops.object.mode_set(mode='EDIT')
ops.curve.subdivide(number_cuts=ncut)
# Return to object mode.
ops.object.mode_set(mode='OBJECT')

Then the bezier points are placed in pts, that we will change with our actual track points. Note that we only change the x,y,z coordinates of the points and we let unchanged the left and right handles. These are automatically computed as pt.handle_left_type and pt.handle_left_type are set to “AUTO” by default.

curve = context.active_object
curve.name = 'TrackCenter'
pts = curve.data.splines[0].bezier_points
npt = len(pts)
# change the coordinates of the bezier points to follow our track
for i in range(npt):
    pt = pts[i]
    pt.co.x = lpts[i][0]
    pt.co.y = lpts[i][1]
    pt.co.z = lpts[i][2]

In the following part of the code we create a basic track element (a plane) that we will duplicate all along the bezier curve. The basic planar element has a length of 1 unit and a width of 5 units. Here we consider that the blender unit is 10 cm, so the track basic element is 10×50 cm. On these basic elements we apply 2 modifiers; First an array modifier is created to duplicate the basic element as many times as required to fill the curve. Second a curve modifier is added to specify the bezier curve will be use to determine the location and orientation of the duplicated track elements. Finally, we apply the two modifiers.

# define the basis track element that we slided along the track
# it is a plane of 1x5 blender units (a cube could have been used too)
ops.mesh.primitive_plane_add (location=(0.0, 0.0, 0.0))
base = context.active_object
base.name = 'TrackElement'
ops.transform.resize(value=(1.0, 5.0, 1.0)) # set 5 units width
# add an array modifier, it will add as many elements as needed to cover
# the curve
ma = base.modifiers.new(name='mod_array_' + base.name, type="ARRAY")
ma.fit_type = "FIT_CURVE"
ma.curve = curve
ma.relative_offset_displace = (1.0,0.0,0.0)
# add a curve modifier to say what curve must be followed
mc = base.modifiers.new(name='mod_curve_' + base.name, type="CURVE")
mc.object = curve
# apply the modifiers
ops.object.modifier_apply(apply_as='DATA', modifier='mod_array_TrackElement')
ops.object.modifier_apply(apply_as='DATA', modifier='mod_curve_TrackElement')

We use the same principle to add the gates at given intervals along the track. The basic gate is build using CSG (Constructive Solide Geometry), see here for details.

The full code create_track_bezier.pycan be found here. To use it, run blender, click on scripting on the top menu bar. Then in the script window menu click on test and on new. In the editing window, you can cut and paste the code create_track_bezier.py. To build the track, click on run script. The result should be as follows:

To change the shape of the track you just have to change the initial definition of the pts array. Before make a new run script, you must remove the existing track. A quick way to do it to go in the window where the track is displayed, press A to select all objects, then press “Delete” key.

When the track has a satisfying shape, you can export it in Wavefront (.obj) format so it can be easily imported in V-REP.

Back to top.

blender/main-blender-create-track.txt · Last modified: 2023/03/31 12:15 by 127.0.0.1