Simple Road Modeller (Proof of Concept)

Here’s a proof of concept road modeller. Feel free to edit, extend, improve if you find it useful.

Usage:
Create a triangulated surface mesh and name it ‘target’
Add a path and name it ‘alignment’
Add a new mesh, name it ‘template’. Edit the mesh into your road shape with side slopes. Add the vertices at the end of your slopes to a vertex group named ‘EndPoints’
Add a ‘Follow Path’ constraint to your template.
Set the begin and end frames for your animation
Run the script and then start your animation

The script uses a frame change handler to create a new mesh at each frame. From the vertices in the ‘EndPoints’ group, the intercept point on the target surface is found using the slope vector of the edge the endpoint is on. When the animation is done running you can select the new meshes and loft them to form the road surface.


import bpy
import mathutils
from mathutils import geometry
import bmesh

def alg_info():
    for obj in bpy.data.objects:
        if obj.name=="alignment":
            alg_len=obj.data.path_duration
    return(alg_len)

def run_model():
    current_scene = bpy.context.scene
    alg_data=alg_info()
    hp=[]
    for obj in bpy.data.objects:
        edgs=[]
        if obj.name=="template":
            bm=bmesh.new()
            bpy.context.scene.objects.active = obj
            loc=obj.location
            me=obj.data
            bpy.ops.object.mode_set(mode='EDIT')
            mat = obj.matrix_world
            bm.from_object(obj,current_scene)
            bm.transform(mat)
            new_mesh=bpy.data.meshes.new("MeshObject")
            bm.to_mesh(new_mesh)
            new_mesh.update()
            drop=bpy.data.objects.new("drop", new_mesh)
            current_scene.objects.link(drop)
            current_scene.update()
            drop.select=True
            bpy.context.scene.objects.active=obj
            bpy.ops.object.vertex_group_copy_to_selected()
            drop.select=False
            for edg in me.edges:
                group_lookup = {g.index: g.name for g in obj.vertex_groups}
                verts = {name: [] for name in group_lookup.values()}
                for v in me.vertices:
                    for g in v.groups:
                        verts[group_lookup[g.group]].append(v.index)
            for ep in verts['EndPoints']:
                for edg in me.edges:
                    k=edg.vertices
                    if ep in k:
                        origin=mat*(me.vertices[ep].co)
                        linea=mat*(me.vertices[k[0]].co)
                        lineb=mat*(me.vertices[k[1]].co)
                        if ep==k[0]:
                            vector=linea-lineb
                        if ep==k[1]:
                            vector=lineb-linea
                        obj.select=False
                        for targ in bpy.data.objects:
                            if targ.name=="target":
                                mat2=targ.matrix_world
                                target=targ.data
                                for tris in target.polygons:
                                    fac=tris.vertices
                                    v1=mat2*(target.vertices[fac[0]].co)
                                    v2=mat2*(target.vertices[fac[1]].co)
                                    v3=mat2*(target.vertices[fac[2]].co)
                                    hitpoint=(mathutils.geometry.intersect_ray_tri(v1,v2,v3,vector,origin,True))
                                    if hitpoint != None:
                                        hp=bm.verts.new(hitpoint)
                                        bep=bm.verts.new(origin)
                                        be=bm.edges.new((bep,hp))
    bpy.ops.object.mode_set(mode='EDIT')
    bmesh.ops.remove_doubles(bm,verts=bm.verts,dist=0.0001)
    bm.to_mesh(new_mesh)
    new_mesh.update()
    bpy.ops.object.mode_set(mode='EDIT')
    obj.select=True
    bpy.ops.object.mode_set(mode='OBJECT')
    bm.free()

def my_handler(scene):
    frame = scene.frame_current
    alg_duration=scene.frame_end
    if frame==alg_duration:
        bpy.ops.screen.animation_cancel()
        bpy.ops.object.mode_set(mode='OBJECT')
        scene.update()
    run_model()  

bpy.app.handlers.frame_change_pre.append(my_handler) 


‘template’ mesh, highlighted verts are in the EndPoints vertex group


‘template’ mesh, ‘alignment’ path, ‘target’ surface, new meshes created at each frame intercepting the original target surface

Attachments

road_modeller.blend (272 KB)

Could please make a short video explaining the concepts of your addon?

Unfortunately I can’t get videos uploaded. I added a couple of screenshots which might help. You create a cross section of the road you want to create. The script projects the EndPoints vertex group to the target surface and creates a new mesh at each frame. Once you merge all of the new meshes together and loft them you have the roadway sitting on top of an embankment that fits onto the original target surface topography.

Now your idea became more clear.