Last active
May 19, 2025 09:48
-
-
Save nightm4re94/d024d3a534de0b8fc482436833b75c83 to your computer and use it in GitHub Desktop.
A Blender script to import cities created with Watabou's Medieval Fantasy City Generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bpy | |
import json | |
import math | |
import random | |
from shapely.geometry import Polygon, Point | |
# Load JSON data | |
with open("D:/Downloads/nightfair.json", "r") as f: | |
city_data = json.load(f) | |
# Preset array of credible building colors for districts | |
building_colors = [ | |
(0.8, 0.2, 0.1, 1), # Classic brick red | |
(0.6, 0.6, 0.6, 1), # Stone gray | |
(1.0, 1.0, 0.8, 1), # Ivory white | |
(0.85, 0.6, 0.2, 1), # Warm ochre | |
(0.2, 0.6, 0.2, 1), # Deep forest green | |
(0.5, 0.7, 1.0, 1), # Coastal blue | |
(0.2, 0.2, 0.2, 1), # Urban charcoal | |
(1.0, 0.4, 0.2, 1), # Sunset orange | |
(0.6, 0.4, 0.8, 1), # Dusty lavender | |
(0.5, 0.5, 0.8, 1), # Slate blue | |
(1.0, 0.7, 0.5, 1), # Soft peach | |
(0.5, 0.8, 0.5, 1), # Seafoam green | |
(0.76, 0.25, 0.13, 1), # Burnt sienna | |
(1.0, 1.0, 1.0, 1), # Crisp white | |
(0.1, 0.1, 0.1, 1), # Charcoal black | |
(1.0, 0.6, 0.2, 1), # Tangerine yellow | |
(0.6, 0.4, 0.2, 1), # Warm taupe | |
(0.5, 0.7, 0.9, 1), # Powder blue | |
(1.0, 0.85, 0.25, 1), # Goldenrod yellow | |
(0.5, 0.6, 0.2, 1), # Moss green | |
(0.72, 0.45, 0.2, 1), # Copper brown | |
(0.5, 0.0, 0.5, 1), # Royal purple | |
(0.94, 0.87, 0.7, 1), # Light almond | |
(1.0, 0.8, 0.0, 1), # Sunflower yellow | |
(0.27, 0.51, 0.71, 1), # Steel blue | |
(1.0, 0.8, 0.9, 1), # Cherry blossom pink | |
(0.94, 0.91, 0.55, 1), # Sandstone beige | |
(0.6, 0.3, 0.2, 1), # Auburn red | |
(0.0, 0.4, 0.2, 1), # Pine green | |
(0.0, 0.28, 0.67, 1), # Cobalt blue | |
(0.44, 0.5, 0.6, 1), # Slate gray | |
(0.6, 1.0, 0.2, 1), # Lime green | |
] | |
# Helper functions | |
def create_material(name, color): | |
mat = bpy.data.materials.new(name=name) | |
mat.use_nodes = True | |
bsdf = mat.node_tree.nodes["Principled BSDF"] | |
bsdf.inputs["Base Color"].default_value = color | |
return mat | |
def create_roof_material(): | |
"""Create and return a roof material.""" | |
# Create the material for the roof | |
roof_material = bpy.data.materials.new(name="Roof_Material") | |
roof_material.use_nodes = True | |
bsdf = roof_material.node_tree.nodes["Principled BSDF"] | |
# Set the base color to a typical roof color (e.g., brownish red) | |
bsdf.inputs["Base Color"].default_value = (0.8, 0.2, 0.1, 1) # Red roof color | |
# Optionally set other properties like roughness and metallic | |
bsdf.inputs["Roughness"].default_value = 0.7 # Slightly rough roof | |
bsdf.inputs["Metallic"].default_value = 0.0 # Not metallic | |
return roof_material | |
def create_tree_material(): | |
"""Create and return a green material for the trees.""" | |
tree_material = bpy.data.materials.new(name="Tree_Material") | |
tree_material.use_nodes = True | |
bsdf = tree_material.node_tree.nodes["Principled BSDF"] | |
# Set the base color to green | |
bsdf.inputs["Base Color"].default_value = (0.1, 0.7, 0.1, 1) # A green color for the tree | |
# Optionally adjust other material properties, e.g., roughness for a natural look | |
bsdf.inputs["Roughness"].default_value = 0.5 # Adjust roughness if needed | |
bsdf.inputs["Metallic"].default_value = 0.3 # Adjust shine if needed | |
return tree_material | |
def create_water_material(): | |
"""Create and return a blue material for the water.""" | |
# Create the material for the water | |
water_material = bpy.data.materials.new(name="Water_Material") | |
water_material.use_nodes = True | |
bsdf = water_material.node_tree.nodes["Principled BSDF"] | |
# Set the base color to blue | |
bsdf.inputs["Base Color"].default_value = (0.2, 0.4, 1.0, 1) # Light blue color for water | |
# Optionally set other properties for water-like appearance | |
bsdf.inputs['Alpha'].default_value = 0.8 # Make it transparent like water | |
bsdf.inputs["Roughness"].default_value = 0.0 # Smooth surface for water | |
bsdf.inputs["Metallic"].default_value = 0.5 # Slightly reflective for water surface | |
return water_material | |
def create_earth_material(): | |
"""Create and return an earth material (brownish, rocky texture).""" | |
# Create the material for the earth | |
earth_material = bpy.data.materials.new(name="Earth_Material") | |
earth_material.use_nodes = True | |
bsdf = earth_material.node_tree.nodes["Principled BSDF"] | |
# Set the base color to a brownish, earthy tone | |
bsdf.inputs["Base Color"].default_value = (0.6, 0.4, 0.2, 1) # Brown color for earth | |
# Set roughness to simulate a natural, non-reflective surface | |
bsdf.inputs["Roughness"].default_value = 0.8 # Rough surface for earth | |
# Optionally adjust specular if needed (usually low for earthy textures) | |
bsdf.inputs["Metallic"].default_value = 0.2 # Low specular for natural material | |
return earth_material | |
def create_field_material(): | |
"""Create and return a green material for the fields.""" | |
# Create the material for the field | |
field_material = bpy.data.materials.new(name="Field_Material") | |
field_material.use_nodes = True | |
bsdf = field_material.node_tree.nodes["Principled BSDF"] | |
# Set the base color to a grassy green | |
bsdf.inputs["Base Color"].default_value = (0.3, 0.7, 0.3, 1) # Grass green color | |
# Set roughness to simulate a natural, matte surface (not reflective) | |
bsdf.inputs["Roughness"].default_value = 0.6 # Rough surface for fields | |
# Optionally adjust specular to simulate a more natural surface | |
bsdf.inputs["Metallic"].default_value = 0.2 # Low specular for a grassy material | |
return field_material | |
def create_green_material(): | |
"""Create and return a green material for greenspaces (like grass, plants, etc.).""" | |
# Create the material for the green | |
green_material = bpy.data.materials.new(name="Green_Material") | |
green_material.use_nodes = True | |
bsdf = green_material.node_tree.nodes["Principled BSDF"] | |
# Set the base color to a lush, vibrant green | |
bsdf.inputs["Base Color"].default_value = (0.2, 0.8, 0.2, 1) # A vibrant green color | |
# Set roughness to simulate a natural, non-reflective surface (grass-like) | |
bsdf.inputs["Roughness"].default_value = 0.5 # Slight roughness for a natural finish | |
# Set specular value (grass often has some subtle reflection) | |
bsdf.inputs["Metallic"].default_value = 0.3 # Slightly reflective, but not too much | |
return green_material | |
def create_stone_material(): | |
"""Create and return a stone-like material for squares.""" | |
# Create the material for the stone | |
stone_material = bpy.data.materials.new(name="Stone_Material") | |
stone_material.use_nodes = True | |
bsdf = stone_material.node_tree.nodes["Principled BSDF"] | |
# Set the base color to a natural stone gray | |
bsdf.inputs["Base Color"].default_value = (0.4, 0.4, 0.4, 1) # Neutral stone gray color | |
# Set roughness to simulate a rough stone surface (not reflective) | |
bsdf.inputs["Roughness"].default_value = 0.8 # Rough stone-like finish | |
# Set specular value to give it a bit of subtle reflection (like wet stones) | |
bsdf.inputs["Metallic"].default_value = 0.3 # Low specular for a stone material | |
# Optionally, add a bump map or a noise texture for more realistic stone texture | |
# You could use a texture or noise pattern here if desired | |
return stone_material | |
def create_plank_material(): | |
"""Create and return a material for the planks.""" | |
plank_material = bpy.data.materials.new(name="Plank_Material") | |
plank_material.use_nodes = True | |
bsdf = plank_material.node_tree.nodes["Principled BSDF"] | |
# Set the base color to a wood-like color (light brown) | |
bsdf.inputs["Base Color"].default_value = (0.6, 0.3, 0.1, 1) # Light brown for wood | |
# Set roughness to simulate a rough wooden texture | |
bsdf.inputs["Roughness"].default_value = 0.8 # Rough wood texture | |
# Set specular for a slightly matte appearance | |
bsdf.inputs["Metallic"].default_value = 0.4 | |
return plank_material | |
def get_district_material(district_name): | |
"""Assign a material based on the district's name using a color palette.""" | |
# Use a hash of the district name to cycle through the color palette | |
color_index = hash(district_name) % len(district_colors) # Ensure a consistent color | |
color = district_colors[color_index] | |
# Create the material using the selected color | |
return create_material(district_name, color) | |
def assign_material(obj, material): | |
if not obj.data.materials: | |
obj.data.materials.append(material) | |
else: | |
obj.data.materials[0] = material | |
def create_tree(location, depth=5, tree_collection=None): | |
# Adjust the z-coordinate to raise the tree by 0.5 * depth | |
x, y, z = location | |
z += 0.5 * depth # Raise the tree by half of its depth | |
# Create the cone (tree) at the adjusted location | |
bpy.ops.mesh.primitive_cone_add(vertices=8, radius1=1, depth=depth, location=(x, y, z)) | |
tree_obj = bpy.context.object | |
tree_obj.name = "Tree" | |
assign_material(tree_obj, create_tree_material()) | |
# Link tree object to the provided collection | |
if tree_collection: | |
tree_collection.objects.link(tree_obj) | |
bpy.context.collection.objects.unlink(tree_obj) # Unlink from the current collection (default collection) | |
return tree_obj | |
def calculate_height(area, min_height=5.0, max_height=20.0): | |
"""Calculate building height based on area, clamped to [min_height, max_height].""" | |
normalized_area = max(0, min(area, 1000)) / 1000 # Normalize area to [0, 1] | |
return min_height + normalized_area * (max_height - min_height) | |
def create_roof(coordinates, base_height, roof_height): | |
""" | |
Create a simple pyramid roof on top of a building. | |
:param coordinates: List of (x, y) tuples defining the building's base polygon. | |
:param base_height: The height of the building. | |
:param roof_height: The height of the roof above the building's top. | |
:return: The created roof object. | |
""" | |
# Calculate the centroid (center) of the building | |
centroid_x = sum(x for x, y in coordinates) / len(coordinates) | |
centroid_y = sum(y for x, y in coordinates) / len(coordinates) | |
# The apex of the pyramid (roof) is at the center of the base + roof height | |
apex = (centroid_x, centroid_y, base_height + roof_height) | |
# Create vertices for the roof: the base vertices and the apex | |
vertices = [] | |
for x, y in coordinates: | |
vertices.append((x, y, base_height)) # Add the base vertices at building height | |
vertices.append(apex) # Add the apex at the top of the building | |
# Create faces for the pyramid: triangles connecting each base vertex to the apex | |
faces = [] | |
num_vertices = len(coordinates) | |
for i in range(num_vertices): | |
# Each face connects the i-th vertex, the (i+1)-th vertex (with wrapping), and the apex | |
next_i = (i + 1) % num_vertices | |
faces.append([i, next_i, num_vertices]) # Each triangle | |
# Create the mesh for the roof | |
mesh = bpy.data.meshes.new("PyramidRoof") | |
mesh.from_pydata(vertices, [], faces) | |
roof_obj = bpy.data.objects.new("Roof", mesh) | |
# Link the roof object to the current collection | |
bpy.context.collection.objects.link(roof_obj) | |
# Apply roof material | |
assign_material(roof_obj, create_roof_material()) | |
return roof_obj | |
def create_building(coordinates, parent_collection): | |
""" | |
Create a building with a roof and group them in a unique collection for the building. | |
:param coordinates: List of (x, y) tuples defining the building's base polygon. | |
:param material: Material to apply to the building. | |
:param parent_collection: The general collection to link the new collection to. | |
""" | |
# Calculate the building footprint area | |
polygon = Polygon(coordinates) | |
area = polygon.area | |
height = calculate_height(area) | |
# Create building base | |
vertices = [(x, y, 0) for x, y in coordinates] + [(x, y, height) for x, y in coordinates] | |
edges = [] | |
faces = [ | |
[i for i in range(len(coordinates))], # Bottom face | |
[i + len(coordinates) for i in range(len(coordinates))] # Top face | |
] | |
faces += [[i, (i + 1) % len(coordinates), (i + 1) % len(coordinates) + len(coordinates), i + len(coordinates)] | |
for i in range(len(coordinates))] # Side faces | |
mesh = bpy.data.meshes.new("Building") | |
mesh.from_pydata(vertices, edges, faces) | |
building_obj = bpy.data.objects.new("Building", mesh) | |
bpy.context.collection.objects.link(building_obj) | |
# Assign material | |
color = random.choice(building_colors) | |
material = create_material(building_obj.name, color) | |
assign_material(building_obj, material) | |
# Create a new collection for the building and its roof | |
building_collection = bpy.data.collections.new(f"Building_{building_obj.name}") | |
parent_collection.children.link(building_collection) # Link this new collection to the parent collection | |
# Always link the building to its new collection | |
building_collection.objects.link(building_obj) | |
# If the building has exactly 4 vertices (for rectangular buildings), add a roof. | |
if len(coordinates) == 4: | |
# Add a pointy roof if the building has 4 vertices | |
roof_obj = create_roof(coordinates, base_height=height, roof_height=height * 0.4) | |
# Link the roof to the same collection as the building | |
building_collection.objects.link(roof_obj) | |
bpy.context.collection.objects.unlink(building_obj) # Unlink from the default collection | |
bpy.context.collection.objects.unlink(roof_obj) # Unlink from the default collection | |
else: | |
# If not rectangular, just add the building to the collection | |
bpy.context.collection.objects.unlink(building_obj) # Unlink from the default collection | |
# Return the new collection for possible further use | |
return building_collection | |
def create_polygon(coordinates, height=0.1, name="Polygon", collection=None): | |
vertices = [(x, y, 0) for x, y in coordinates] + [(x, y, height) for x, y in coordinates] | |
edges = [] | |
faces = [ | |
[i for i in range(len(coordinates))], | |
[i + len(coordinates) for i in range(len(coordinates))] | |
] | |
faces += [[i, (i + 1) % len(coordinates), (i + 1) % len(coordinates) + len(coordinates), i + len(coordinates)] | |
for i in range(len(coordinates))] | |
mesh = bpy.data.meshes.new(name) | |
mesh.from_pydata(vertices, edges, faces) | |
obj = bpy.data.objects.new(name, mesh) | |
bpy.context.collection.objects.link(obj) | |
# Link the object to the provided collection | |
if collection: | |
collection.objects.link(obj) | |
bpy.context.collection.objects.unlink(obj) # Unlink from the default collection | |
return obj | |
def create_plank_line(coordinates, width, collection=None): | |
"""Create a 3D plank along a line with a given width.""" | |
# Calculate the direction vector from the first and last point of the line | |
x1, y1 = coordinates[0] | |
x2, y2 = coordinates[-1] | |
# Vector between the two points | |
dx = x2 - x1 | |
dy = y2 - y1 | |
length = math.sqrt(dx**2 + dy**2) | |
# Normalize direction | |
direction_x = dx / length | |
direction_y = dy / length | |
# Create a plank (cube stretched along the line) | |
bpy.ops.mesh.primitive_cube_add(size=1, location=((x1 + x2) / 2, (y1 + y2) / 2, 0), enter_editmode=False) | |
plank = bpy.context.object | |
plank.scale = (length / 2, width / 2, width / 2) | |
# Rotate the plank so it matches the direction of the line | |
angle = math.atan2(dy, dx) | |
plank.rotation_euler[2] = angle | |
# Apply the material to the plank | |
plank_material = create_plank_material() | |
plank.data.materials.append(plank_material) | |
# Link the plank to the specified collection | |
if collection: | |
collection.objects.link(plank) | |
bpy.context.collection.objects.unlink(plank) # Unlink from the default collection | |
return plank | |
def create_curve(coordinates, width, collection, object_name="Curve"): | |
"""Create a curve (e.g., road or river) with given coordinates and width.""" | |
# Create a NURBS path from the coordinates | |
bpy.ops.object.select_all(action='DESELECT') # Deselect all objects | |
curve_data = bpy.data.curves.new(object_name, type='CURVE') | |
curve_data.dimensions = '2D' # Ensure the curve is flat along the XY plane | |
# Create a path (nurbs curve) | |
spline = curve_data.splines.new('POLY') | |
spline.points.add(count=len(coordinates) - 1) # Add points based on the coordinates | |
# Set curve points | |
for i, (x, y) in enumerate(coordinates): | |
spline.points[i].co = (x, y, 0, 1) # Set points in 3D space (last value is the weight, which is 1) | |
# Create an object from the curve | |
curve_object = bpy.data.objects.new(object_name, curve_data) | |
bpy.context.collection.objects.link(curve_object) # Link to the current scene collection | |
# Set the bevel depth to define the width of the road/rivers (extrude the curve to give it width) | |
# curve_object.data.bevel_depth = width / 2 # Bevel depth defines the width of the curve | |
# curve_object.data.bevel_resolution = 8 # Resolution of bevel (smoothness) | |
# curve_object.data.extrude = 0 # Set extrusion to 0 to keep the curve flat (no vertical height) | |
# Link the curve object to the specified collection | |
collection.objects.link(curve_object) | |
bpy.context.collection.objects.unlink(curve_object) # Unlink from the current collection | |
return curve_object | |
def create_river_polygon(coordinates, width, collection=None): | |
"""Create a flat river polygon based on coordinates and width.""" | |
vertices = [] | |
faces = [] | |
# Loop through the coordinates and create two vertices for each segment | |
for i in range(len(coordinates)): | |
x1, y1 = coordinates[i] | |
# For each coordinate, calculate the perpendicular direction and spawn two vertices | |
if i > 0: | |
x0, y0 = coordinates[i-1] # Previous point (for direction) | |
elif i < len(coordinates) - 1: | |
x0, y0 = coordinates[i + 1] # Next point (for direction) | |
else: | |
x0, y0 = coordinates[i] # Just use the current coordinate if it's the only one | |
# Calculate the direction vector from (x0, y0) to (x1, y1) | |
dx = x1 - x0 | |
dy = y1 - y0 | |
length = math.sqrt(dx**2 + dy**2) | |
# Normalize the direction vector to get the unit vector | |
direction_x = dx / length | |
direction_y = dy / length | |
# Perpendicular direction (rotate by 90 degrees) | |
perp_x = -direction_y | |
perp_y = direction_x | |
# Calculate the left and right offset points by half the width | |
offset_x1 = x1 + perp_x * (width / 2) | |
offset_y1 = y1 + perp_y * (width / 2) | |
offset_x2 = x1 - perp_x * (width / 2) | |
offset_y2 = y1 - perp_y * (width / 2) | |
# Add the two vertices to the list | |
vertices.append((offset_x1, offset_y1, 0)) # Left vertex | |
vertices.append((offset_x2, offset_y2, 0)) # Right vertex | |
# Loop through the coordinates to create faces between the left and right vertices | |
for i in range(0, len(vertices) - 2, 2): | |
faces.append([i, i + 1, i + 3, i + 2]) # Form faces between adjacent left and right vertices | |
# Create mesh for the river | |
river_mesh = bpy.data.meshes.new("River") | |
river_mesh.from_pydata(vertices, [], faces) | |
river_obj = bpy.data.objects.new("River", river_mesh) | |
bpy.context.collection.objects.link(river_obj) | |
# Link the river to the collection | |
if collection: | |
collection.objects.link(river_obj) | |
bpy.context.collection.objects.unlink(river_obj) # Unlink from the default collection | |
return river_obj | |
def create_tower(location, height=14, radius=10, collection=None): | |
"""Create a tower (cylinder) at the specified location.""" | |
total_height = height * 1.2 | |
if len(location) == 2: | |
location = (*location, total_height/2) # Set z to height/2 if the location is 2D (usually the cylinder is centered at z=0) | |
bpy.ops.mesh.primitive_cylinder_add(radius=radius, depth=total_height, location=location) | |
tower = bpy.context.object # The newly created tower object | |
tower_material = create_material("TowerMaterial", (0.3, 0.3, 0.3, 1)) | |
assign_material(tower,tower_material) | |
# Add the tower to the specified collection | |
if collection: | |
collection.objects.link(tower) | |
bpy.context.collection.objects.unlink(tower) # Unlink from the default collection | |
return tower | |
def spawn_towers_at_wall_vertices(wall_coordinates, height, collection=None): | |
"""Spawn a tower at each vertex of a wall.""" | |
for vertex in wall_coordinates: | |
# Spawn a tower at each vertex (x, y, z) | |
create_tower(vertex, height=height, radius=4, collection=collection) | |
def create_wall(coordinates, thickness=0.5, height=12.0, parent_collection=None): | |
wall_segments = [] | |
spawn_towers_at_wall_vertices(coordinates, height=height, collection=parent_collection) | |
for start, end in zip(coordinates, coordinates[1:] + [coordinates[0]]): # Loop back to the start | |
x1, y1 = start | |
x2, y2 = end | |
# Calculate wall vertices | |
dx, dy = x2 - x1, y2 - y1 | |
length = math.sqrt(dx**2 + dy**2) | |
nx, ny = dy / length * thickness / 2, -dx / length * thickness / 2 # Perpendicular vector for thickness | |
vertices = [ | |
(x1 - nx, y1 - ny, 0), (x2 - nx, y2 - ny, 0), # Outer base | |
(x1 + nx, y1 + ny, 0), (x2 + nx, y2 + ny, 0), # Inner base | |
(x1 - nx, y1 - ny, height), (x2 - nx, y2 - ny, height), # Outer top | |
(x1 + nx, y1 + ny, height), (x2 + nx, y2 + ny, height) # Inner top | |
] | |
edges = [] | |
faces = [ | |
[0, 1, 5, 4], # Outer face | |
[2, 3, 7, 6], # Inner face | |
[0, 2, 6, 4], # Left face | |
[1, 3, 7, 5], # Right face | |
[4, 5, 7, 6], # Top face | |
[0, 1, 3, 2] # Bottom face | |
] | |
mesh = bpy.data.meshes.new("Wall") | |
mesh.from_pydata(vertices, edges, faces) | |
wall_obj = bpy.data.objects.new("Wall", mesh) | |
if parent_collection: | |
parent_collection.objects.link(wall_obj) | |
wall_segments.append(wall_obj) | |
return wall_segments | |
# Create Collections | |
walls_collection = bpy.data.collections.new("Walls") | |
bpy.context.scene.collection.children.link(walls_collection) | |
buildings_collection = bpy.data.collections.new("Buildings") | |
bpy.context.scene.collection.children.link(buildings_collection) | |
districts_collection = bpy.data.collections.new("Districts") | |
bpy.context.scene.collection.children.link(districts_collection) | |
roads_collection = bpy.data.collections.new("Roads") | |
bpy.context.scene.collection.children.link(roads_collection) | |
water_collection = bpy.data.collections.new("Water") | |
bpy.context.scene.collection.children.link(water_collection) | |
earth_collection = bpy.data.collections.new("Earth") | |
bpy.context.scene.collection.children.link(earth_collection) | |
rivers_collection = bpy.data.collections.new("Rivers") | |
bpy.context.scene.collection.children.link(rivers_collection) | |
prisms_collection = bpy.data.collections.new("Prisms") | |
bpy.context.scene.collection.children.link(prisms_collection) | |
trees_collection = bpy.data.collections.new("Trees") | |
bpy.context.scene.collection.children.link(trees_collection) | |
squares_collection = bpy.data.collections.new("Squares") | |
bpy.context.scene.collection.children.link(squares_collection) | |
greens_collection = bpy.data.collections.new("Greens") | |
bpy.context.scene.collection.children.link(greens_collection) | |
fields_collection = bpy.data.collections.new("Fields") | |
bpy.context.scene.collection.children.link(fields_collection) | |
planks_collection = bpy.data.collections.new("Planks") | |
bpy.context.scene.collection.children.link(planks_collection) | |
# Process features | |
for feature in city_data["features"]: | |
feature_type = feature["type"] | |
feature_id = feature.get("id", "") | |
if feature_id == "trees" and feature_type == "MultiPoint": | |
for x, y in feature["coordinates"]: | |
create_tree((x, y, 0), tree_collection=trees_collection) | |
elif feature_id == "districts" and feature_type == "GeometryCollection": | |
for district in feature["geometries"]: | |
if district["type"] == "Polygon": | |
district_name = district.get('name', 'Unnamed') | |
district_polygon = create_polygon(square[0], height=0.1, name="District", collection=districts_collection) | |
elif feature_id == "buildings" and feature_type == "MultiPolygon": | |
for building in feature["coordinates"]: | |
for coordinates in building: | |
building_collection = create_building(coordinates, buildings_collection) | |
# Processing the 'rivers' feature (now uses create_curve) | |
elif feature_id == "rivers" and feature_type == "GeometryCollection": | |
for river in feature["geometries"]: | |
if river["type"] == "LineString": | |
# create_curve(river["coordinates"], river["width"], river_collection, object_name="River") | |
river_polygon = create_river_polygon(river["coordinates"], river["width"], collection=rivers_collection) | |
assign_material(river_polygon, create_water_material()) | |
# Processing the 'roads' feature (now uses create_curve) | |
elif feature_id == "roads" and feature_type == "GeometryCollection": | |
for road in feature["geometries"]: | |
if road["type"] == "LineString": | |
create_curve(road["coordinates"], road["width"], roads_collection, object_name="Road") | |
elif feature_id == "walls" and feature_type == "GeometryCollection": | |
for wall in feature["geometries"]: | |
if wall["type"] == "Polygon": | |
segments = create_wall(wall["coordinates"][0], thickness=0.5, height=12.0, parent_collection=walls_collection) | |
for wall_polygon in segments: | |
assign_material(wall_polygon, create_stone_material()) | |
elif feature_id == "squares" and feature_type == "MultiPolygon": | |
for square in feature["coordinates"]: | |
square_polygon = create_polygon(square[0], height=0.6, name="Square", collection=squares_collection) | |
assign_material(square_polygon, create_stone_material()) | |
elif feature_id == "greens" and feature_type == "MultiPolygon": | |
for green in feature["coordinates"]: | |
green_polygon = create_polygon(green[0], height=0.6, name="Green", collection=greens_collection) | |
assign_material(green_polygon, create_green_material()) | |
elif feature_id == "fields" and feature_type == "MultiPolygon": | |
for field in feature["coordinates"]: | |
field_polygon = create_polygon(field[0], height=0.6, name="Field", collection=fields_collection) | |
assign_material(field_polygon, create_field_material()) | |
elif feature_id == "earth" and feature_type == "Polygon": | |
earth_coordinates = feature["coordinates"][0] | |
earth_polygon = create_polygon(earth_coordinates, height=0.1, name="Earth", collection=earth_collection) | |
assign_material(earth_polygon, create_earth_material()) | |
elif feature_id == "water" and feature_type == "MultiPolygon": | |
for water in feature["coordinates"]: | |
for coordinates in water: | |
water_polygon = create_polygon(coordinates, height=0.15, name="Water", collection=water_collection) | |
assign_material(water_polygon, create_water_material()) | |
elif feature_id == "prisms" and feature_type == "MultiPolygon": | |
for prism in feature["coordinates"]: | |
for coordinates in prism: | |
prism_polygon = create_polygon(coordinates, height=5.0, name="Prism", collection=prisms_collection) | |
assign_material(prism_polygon, create_stone_material()) | |
elif feature["id"] == "planks" and feature["type"] == "GeometryCollection": | |
for plank in feature["geometries"]: | |
if plank["type"] == "LineString": | |
# Get the coordinates and width of the plank | |
coordinates = plank["coordinates"] | |
width = plank["width"] | |
# Create the plank line in Blender | |
create_plank_line(coordinates, width, collection=planks_collection) | |
# Final adjustments | |
bpy.ops.object.select_all(action="DESELECT") | |
# Final adjustments | |
bpy.ops.object.select_all(action="DESELECT") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment