Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create python script to automate Blender process #36

Open
ProbableTrain opened this issue May 1, 2020 · 5 comments
Open

Create python script to automate Blender process #36

ProbableTrain opened this issue May 1, 2020 · 5 comments
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed

Comments

@ProbableTrain
Copy link
Owner

maps.probabletrain.com/#/stl describes a very specific process for creating the model. This could be automated with a Blender python script.

@ProbableTrain ProbableTrain added enhancement New feature or request help wanted Extra attention is needed good first issue Good for newcomers labels May 1, 2020
@Calinou
Copy link

Calinou commented Nov 29, 2020

I think providing some example premade .blend files could be useful as well, so that you can test scalability of various lighting/GI techniques in various 3D engines.

@Ghurir
Copy link

Ghurir commented Aug 16, 2021

I wrote this script for my self, but cutting the roads is commented out as it doesn't work. That is because the domain and roads don't get imported aligned. I am not sure why
The seacoast and rivercoast are made differently than in the tutorial. By displacing a new mesh.
scriptcity

#bpy is for general context api
#bmesh is the newer faster way to edit meshes
import bpy, bmesh
# ImportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ImportHelper
#The Property's and operator are for linking with the ui
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator
#os is used once for getting the directory name, i don't know how to do without :(
import os

context = bpy.context
scene = context.scene

def radians(x):
    return x*0.0174532925

#t.u.c.f: Tomesh Update Clear Free
def tucf(mesh, tempMesh):
    tempMesh.to_mesh(mesh)
    mesh.update()
    tempMesh.clear()
    tempMesh.free() # free and prevent further access
    
def duplicate_obj(obj1, name):
    me = obj1.data # use current object's data
    me_copy = me.copy()

    obj2 = bpy.data.objects.new(name, me_copy)
    # Link object to the collection in context
    context.collection.objects.link(obj2)
    obj2.location = obj1.location
    # Update the view layer
    context.view_layer.update()
    
    return obj2 #the duplicate object

#extrudes mesh along a vector
def extrudeObj(obj, vect):
    bm = bmesh.new()   # create an empty BMesh
    mesh = obj.data
    bm.from_mesh(mesh)   # fill it in from a Mesh
    r = bmesh.ops.extrude_face_region(bm, geom=bm.faces[:])
    verts = [e for e in r['geom'] if isinstance(e, bmesh.types.BMVert)]
    bmesh.ops.translate(bm, vec = vect, verts=verts)
    tucf(mesh, bm)


class ImportCityData(Operator, ImportHelper):
    """Import city generated on https://probabletrain.itch.io/city-generator"""
    bl_idname = "import_city.folder"  # important since its how bpy.ops.import_city.folder is constructed
    bl_label = "Import City Folder"

    file_path: StringProperty(
        subtype='DIR_PATH',
        options={'HIDDEN'},
        maxlen=255,  # Max internal buffer length, longer would be clamped.
    )

    def execute(self, context):
        scene = context.scene
        
        print(self.filepath)
        
        models_path = os.path.dirname(self.filepath)
        models = ["blocks.stl", "buildings.stl", "coastline.stl", "domain.stl", "river.stl", "roads.stl", "sea.stl"]
        
        def import_models(directory, model_names):
            stl_models = []
            for name in model_names:
                path = directory + "\\"+ name
                bpy.ops.import_mesh.stl(filepath=path)
                stl_models. append(context.object)
            return stl_models
                
        
        blocks, buildings, coastline, domain, river, roads, sea = import_models(models_path, models)
        
        #set the origins to the object geometery
        bpy.ops.object.select_all(action='SELECT')
        bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
        bpy.ops.object.select_all(action='DESELECT')
        
#        extrudeObj(roads,(0,0,1))
#        roads.location.z=-0.5        
        #currently not really working
#        bpy.ops.object.select_all(action='DESELECT')
#        context.view_layer.objects.active = roads
#        bpy.ops.object.mode_set(mode='EDIT')
#        bpy.ops.mesh.separate(type='LOOSE')
#        bpy.ops.object.mode_set(mode='OBJECT')
#        all_roads = context.selected_objects
#        all_roads.append(context.active_object)
#        coll = bpy.data.collections.new("roads")
#        scene.collection.children.link(coll)
#        
#        bpy.ops.object.select_all(action='DESELECT')
#        context.view_layer.objects.active = domain
#        
#        for road in all_roads:
#            tmpBLN = domain.modifiers.new(name="Boolean", type='BOOLEAN')
#            tmpBLN.object = road
#            bpy.ops.object.modifier_apply(modifier=tmpBLN.name)
#            old_collections = road.users_collection
#            coll.objects.link(road)
#            for collection in old_collections: #unlink from all  precedent obj collections
#                collection.objects.unlink(road)
#
#        roadsSolid = roads.modifiers.new(name="Solidify", type='SOLIDIFY')
#        roadsSolid.thickness = -0.1
#        roads.location.z = 0.001
        
        # extrude river&sea and bolean from domain #
        extrudeObj(river, (0,0,1))
        river.location.z = -0.5
        #
        sea.scale = (1.01, 1.01, 1)
        extrudeObj(sea, (0,0,1))
        sea.location.z = -0.5
        #
        BLNsea = domain.modifiers.new(name="Boolean", type='BOOLEAN')
        BLNsea.object = sea
        BLNriver = domain.modifiers.new(name="Boolean", type='BOOLEAN')
        BLNriver.object = river
        domainSolid = domain.modifiers.new(name="Solidify", type='SOLIDIFY')
        domainSolid.thickness = 1
        
        #this mesh will become the coastline and river shore
        smoothCoastline = duplicate_obj(domain, "shore")
        smoothCoastline.scale =(1.2,1.2,1)
        #
        mesh = smoothCoastline.data
        newbm = bmesh.new()
        newbm.from_mesh(mesh)
        bmesh.ops.dissolve_limit(newbm, angle_limit=radians(5), verts=newbm.verts, edges=newbm.edges)
        tucf(mesh, newbm)
        #The next part is to cut the shore object into a grid
        bpy.ops.object.select_all(action='DESELECT')
        context.view_layer.objects.active = smoothCoastline
        bpy.ops.object.mode_set(mode='EDIT')
        extremeX = [0, 0]
        extremeY = [0, 0]
        VertsX = []
        VertsY = []
        for vert in mesh.vertices:
            VertsX.append(vert.co.x)
            VertsY.append(vert.co.y)
        #
        extremeX[0] = min(VertsX)
        extremeX[1] = max(VertsX)
        extremeY[0] = min(VertsY)
        extremeY[1] = max(VertsY)
        #   
        bm = bmesh.from_edit_mesh(context.object.data)
        for i in range(round(extremeY[0]*10), round(extremeY[1]*10), 1):
            y = i/10
            bmesh.ops.bisect_plane(bm, geom=bm.verts[:]+bm.edges[:]+bm.faces[:], plane_co=(0,y,0), plane_no=(0,1,0))
        #
        for i in range(round(extremeX[0]*10), round(extremeX[1]*10), 1):
            x = i/10
            bmesh.ops.bisect_plane(bm, geom=bm.verts[:]+bm.edges[:]+bm.faces[:], plane_co=(x,0,0), plane_no=(1,0,0))
        #
        bmesh.update_edit_mesh(context.object.data)
        bpy.ops.object.mode_set(mode='OBJECT')
        #Use the domain to displace the shore smoothly
        new_vertex_group = smoothCoastline.vertex_groups.new(name='Group')
        all_verts = list(range(0,len(mesh.vertices)))
        new_vertex_group.add(all_verts, 1.0, 'ADD')
        #
        WeightProx = smoothCoastline.modifiers.new(name="VertexWeightProximity", type='VERTEX_WEIGHT_PROXIMITY')
        WeightProx.target = domain
        WeightProx.vertex_group = "Group"
        WeightProx.proximity_mode = 'GEOMETRY'
        WeightProx.proximity_geometry = {'EDGE'}
        WeightProx.falloff_type = 'SMOOTH'
        #
        displace = smoothCoastline.modifiers.new(name="displace", type='DISPLACE')
        displace.vertex_group = "Group"
        displace.strength = -1
        
        
        return {'FINISHED'}



# For adding to ui or calling function if its directly from script editor
def menu_func_import(self, context):
    self.layout.operator(ImportCityData.bl_idname, text="City importer")


def register():
    bpy.utils.register_class(ImportCityData)
    bpy.types.TOPBAR_MT_file_import.append(menu_func_import)


def unregister():
    bpy.utils.unregister_class(ImportSomeData)
    bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)


if __name__ == "__main__":
    #register()

    # test call
    bpy.ops.import_city.folder('INVOKE_DEFAULT')

@Slluxx
Copy link

Slluxx commented Aug 4, 2023

How do i use this? I am not good with blender and just want to generate a simply city so it can be imported elsewhere.

@Ghurir
Copy link

Ghurir commented Aug 8, 2023

you can just go to this link https://probabletrain.itch.io/city-generator
and download the stl. And then import it wherever you want. The blender part is just how you can process the models.

@Slluxx
Copy link

Slluxx commented Aug 8, 2023

you can just go to this link https://probabletrain.itch.io/city-generator and download the stl. And then import it wherever you want. The blender part is just how you can process the models.

I need a city in fbx format but to get that, i would need to work with the stl files in blender and make them "work together", however i know next to nothing about blender processing. I just need a model of a simple city the way its displayed in city-generator, preferably via an automated process

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

4 participants