Загрузка данных


Blender Python script: stylized realistic exterior model of 2S19 Msta-S

How to use: Blender > Scripting > New > paste this script > Run Script

This creates a non-functional visual 3D model for renders/games/concepts.

import bpy import math from mathutils import Vector

-----------------------------

Scene cleanup

-----------------------------

bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete()

-----------------------------

Helpers

-----------------------------

def make_mat(name, color, roughness=0.65, metallic=0.0): mat = bpy.data.materials.new(name) mat.use_nodes = True bsdf = mat.node_tree.nodes.get("Principled BSDF") bsdf.inputs["Base Color"].default_value = color bsdf.inputs["Roughness"].default_value = roughness bsdf.inputs["Metallic"].default_value = metallic return mat

mat_green = make_mat("mat_olive_green", (0.20, 0.29, 0.16, 1), 0.82) mat_dark_green = make_mat("mat_dark_olive", (0.10, 0.16, 0.09, 1), 0.86) mat_brown = make_mat("mat_camo_brown", (0.23, 0.16, 0.09, 1), 0.88) mat_black = make_mat("mat_rubber_black", (0.015, 0.014, 0.012, 1), 0.92) mat_dark_metal = make_mat("mat_dark_metal", (0.05, 0.055, 0.052, 1), 0.62, 0.2) mat_steel = make_mat("mat_worn_steel", (0.30, 0.31, 0.29, 1), 0.48, 0.25) mat_glass = make_mat("mat_dark_glass", (0.02, 0.05, 0.045, 1), 0.25) mat_sand = make_mat("mat_dust_sand", (0.52, 0.45, 0.31, 1), 0.95)

def assign(obj, mat): obj.data.materials.append(mat) return obj

def cube_obj(name, loc, scale, mat, bevel=0.0): bpy.ops.mesh.primitive_cube_add(size=1, location=loc) obj = bpy.context.object obj.name = name obj.dimensions = scale bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) assign(obj, mat) if bevel > 0: mod = obj.modifiers.new("soft bevel", "BEVEL") mod.width = bevel mod.segments = 2 mod.affect = 'EDGES' obj.modifiers.new("weighted normals", "WEIGHTED_NORMAL") return obj

def cyl_obj(name, loc, radius, depth, mat, vertices=48, rotation=(0, 0, 0), bevel=False): bpy.ops.mesh.primitive_cylinder_add(vertices=vertices, radius=radius, depth=depth, location=loc, rotation=rotation) obj = bpy.context.object obj.name = name assign(obj, mat) if bevel: mod = obj.modifiers.new("rim bevel", "BEVEL") mod.width = 0.025 mod.segments = 2 obj.modifiers.new("weighted normals", "WEIGHTED_NORMAL") return obj

def torus_obj(name, loc, major, minor, mat, rotation=(0, 0, 0)): bpy.ops.mesh.primitive_torus_add(major_radius=major, minor_radius=minor, major_segments=72, minor_segments=12, location=loc, rotation=rotation) obj = bpy.context.object obj.name = name assign(obj, mat) obj.modifiers.new("weighted normals", "WEIGHTED_NORMAL") return obj

def wedge_mesh(name, verts, faces, mat): mesh = bpy.data.meshes.new(name + "Mesh") mesh.from_pydata(verts, [], faces) mesh.update() obj = bpy.data.objects.new(name, mesh) bpy.context.collection.objects.link(obj) assign(obj, mat) obj.modifiers.new("weighted normals", "WEIGHTED_NORMAL") return obj

def add_camouflage_patch(name, loc, scale, mat, rot_z=0): obj = cube_obj(name, loc, scale, mat, bevel=0.015) obj.rotation_euler[2] = math.radians(rot_z) return obj

-----------------------------

Base vehicle proportions

Coordinate system: X length, Y width, Z height

-----------------------------

Tracks and lower hull

cube_obj("left_track_belt", (0, -1.55, 0.55), (6.8, 0.55, 0.80), mat_black, bevel=0.08) cube_obj("right_track_belt", (0, 1.55, 0.55), (6.8, 0.55, 0.80), mat_black, bevel=0.08) cube_obj("lower_hull", (0, 0, 0.85), (6.45, 2.65, 0.78), mat_dark_green, bevel=0.07)

Upper hull with simplified sloped front

cube_obj("upper_hull_main", (-0.15, 0, 1.42), (5.75, 2.45, 0.72), mat_green, bevel=0.09)

Sloped front wedge/glacis

verts = [ (2.55, -1.225, 1.08), (3.35, -1.225, 1.08), (3.35, 1.225, 1.08), (2.55, 1.225, 1.08), (2.25, -1.225, 1.78), (3.15, -1.225, 1.78), (3.15, 1.225, 1.78), (2.25, 1.225, 1.78) ] faces = [(0,1,2,3), (4,7,6,5), (0,4,5,1), (1,5,6,2), (2,6,7,3), (3,7,4,0)] wedge_mesh("sloped_front_glacis", verts, faces, mat_green)

Rear engine deck and grilles

cube_obj("rear_engine_deck", (-2.65, 0, 1.85), (1.05, 2.25, 0.18), mat_dark_green, bevel=0.035) for i, y in enumerate([-0.75, -0.35, 0.05, 0.45, 0.85]): cube_obj(f"engine_grille_{i}", (-2.65, y, 1.96), (0.82, 0.08, 0.035), mat_black, bevel=0.01)

Side skirts

cube_obj("left_side_skirt", (0.05, -1.87, 1.02), (6.35, 0.16, 0.55), mat_green, bevel=0.035) cube_obj("right_side_skirt", (0.05, 1.87, 1.02), (6.35, 0.16, 0.55), mat_green, bevel=0.035)

Road wheels, sprockets, idlers

wheel_xs = [-2.55, -1.85, -1.15, -0.45, 0.25, 0.95, 1.65, 2.35] for side, y in [("L", -1.88), ("R", 1.88)]: for idx, x in enumerate(wheel_xs): cyl_obj(f"{side}road_wheel{idx}", (x, y, 0.52), 0.31, 0.16, mat_dark_metal, 40, rotation=(math.radians(90), 0, 0), bevel=True) cyl_obj(f"{side}road_wheel_hub{idx}", (x, y + (-0.09 if y < 0 else 0.09), 0.52), 0.12, 0.08, mat_steel, 32, rotation=(math.radians(90), 0, 0), bevel=True) for label, x in [("front_idler", 3.05), ("rear_sprocket", -3.05)]: torus_obj(f"{side}_{label}tire", (x, y, 0.58), 0.29, 0.055, mat_black, rotation=(math.radians(90), 0, 0)) cyl_obj(f"{side}{label}_hub", (x, y, 0.58), 0.18, 0.18, mat_dark_metal, 40, rotation=(math.radians(90), 0, 0), bevel=True)

Track pads: bottom and top

for side, y in [("L", -1.90), ("R", 1.90)]: for idx in range(22): x = -3.15 + idx * 0.30 cube_obj(f"{side}bottom_track_pad{idx}", (x, y, 0.14), (0.22, 0.62, 0.075), mat_dark_metal, bevel=0.012) cube_obj(f"{side}top_track_pad{idx}", (x, y, 0.96), (0.22, 0.58, 0.060), mat_dark_metal, bevel=0.01)

Turret: angular mass plus rear bustle

cube_obj("turret_base", (0.30, 0, 2.25), (2.75, 2.05, 0.92), mat_green, bevel=0.11) cube_obj("turret_rear_bustle", (-0.95, 0, 2.24), (1.05, 2.18, 0.82), mat_green, bevel=0.08) cube_obj("turret_front_armor_block", (1.70, 0, 2.22), (0.72, 1.52, 0.70), mat_dark_green, bevel=0.07)

Turret roof details

cyl_obj("commander_hatch_ring", (0.28, -0.55, 2.78), 0.30, 0.07, mat_dark_metal, 48, bevel=True) cyl_obj("loader_hatch_ring", (-0.55, 0.52, 2.78), 0.25, 0.07, mat_dark_metal, 48, bevel=True) cube_obj("roof_periscope_1", (0.62, -0.92, 2.82), (0.25, 0.12, 0.10), mat_glass, bevel=0.025) cube_obj("roof_periscope_2", (0.95, -0.62, 2.82), (0.22, 0.11, 0.09), mat_glass, bevel=0.02) cube_obj("small_roof_box", (-0.92, -0.30, 2.84), (0.34, 0.30, 0.16), mat_dark_green, bevel=0.03)

Main gun barrel and mantlet: visual only

cyl_obj("gun_mantlet", (2.05, 0, 2.28), 0.34, 0.52, mat_dark_green, 56, rotation=(0, math.radians(90), 0), bevel=True) cyl_obj("gun_recoil_sleeve", (2.52, 0, 2.28), 0.20, 0.90, mat_dark_metal, 56, rotation=(0, math.radians(90), 0), bevel=True) cyl_obj("main_barrel", (4.80, 0, 2.28), 0.105, 4.75, mat_steel, 64, rotation=(0, math.radians(90), 0), bevel=True) cyl_obj("barrel_dark_bore", (7.20, 0, 2.28), 0.088, 0.035, mat_black, 48, rotation=(0, math.radians(90), 0), bevel=True)

Muzzle brake approximation

cube_obj("muzzle_brake_block", (7.08, 0, 2.28), (0.22, 0.46, 0.26), mat_dark_metal, bevel=0.03) cube_obj("muzzle_brake_slot_left", (7.09, -0.24, 2.28), (0.25, 0.06, 0.17), mat_black, bevel=0.01) cube_obj("muzzle_brake_slot_right", (7.09, 0.24, 2.28), (0.25, 0.06, 0.17), mat_black, bevel=0.01)

External storage boxes and fuel drums

for i, y in enumerate([-1.22, 1.22]): cube_obj(f"side_storage_box_{i}", (-1.75, y, 1.74), (1.20, 0.30, 0.32), mat_dark_green, bevel=0.04) cube_obj(f"side_tool_box_{i}", (0.05, y, 1.80), (0.86, 0.25, 0.25), mat_green, bevel=0.035)

for i, y in enumerate([-0.42, 0.42]): cyl_obj(f"rear_fuel_drum_{i}", (-3.35, y, 1.34), 0.22, 0.60, mat_dark_green, 40, rotation=(0, math.radians(90), 0), bevel=True) cube_obj(f"rear_fuel_drum_strap_{i}", (-3.35, y, 1.34), (0.64, 0.035, 0.49), mat_black, bevel=0.005)

Antenna and small turret fittings

cyl_obj("antenna_base", (-0.82, 0.92, 2.82), 0.055, 0.09, mat_dark_metal, 24, bevel=True) cyl_obj("antenna_rod", (-0.82, 0.92, 3.48), 0.012, 1.25, mat_black, 16, bevel=False)

Tow hooks / lamps

for y in [-0.75, 0.75]: cube_obj("front_lamp", (3.28, y, 1.48), (0.10, 0.18, 0.12), mat_glass, bevel=0.025) torus_obj("front_tow_hook", (3.30, y * 0.72, 0.92), 0.075, 0.018, mat_dark_metal, rotation=(0, math.radians(90), 0))

Camouflage patches: irregular-looking low-profile plates on hull/turret

patches = [ ("camo_hull_1", (0.60, -0.55, 1.80), (1.15, 0.035, 0.28), mat_brown, -12), ("camo_hull_2", (-1.25, 0.62, 1.82), (1.00, 0.035, 0.30), mat_dark_green, 18), ("camo_turret_1", (0.55, -1.04, 2.42), (0.85, 0.035, 0.28), mat_brown, 10), ("camo_turret_2", (-0.38, 1.04, 2.47), (1.00, 0.035, 0.25), mat_brown, -8), ("camo_front", (2.72, -0.10, 1.82), (0.50, 0.035, 0.22), mat_brown, 0), ] for p in patches: add_camouflage_patch(*p)

Dust/mud accents near tracks

for side, y in [("L", -2.00), ("R", 2.00)]: for idx, x in enumerate([-2.6, -1.7, -0.8, 0.15, 1.15, 2.15]): cube_obj(f"{side}mud_patch{idx}", (x, y, 0.32), (0.55, 0.035, 0.18), mat_sand, bevel=0.02)

Ground plane

cube_obj("matte_ground", (0.7, 0, -0.04), (11.5, 6.5, 0.04), mat_sand, bevel=0)

Add a small label plate, not on the vehicle body

font_curve = bpy.data.curves.new("label_curve", type="FONT") font_curve.body = "2S19 Msta-S — visual non-functional model" font_curve.align_x = 'CENTER' font_curve.size = 0.18 font_obj = bpy.data.objects.new("label_text", font_curve) bpy.context.collection.objects.link(font_obj) font_obj.location = (0.6, -2.9, 0.02) font_obj.rotation_euler = (math.radians(90), 0, 0) font_obj.data.materials.append(mat_black)

Lighting

bpy.ops.object.light_add(type='AREA', location=(1.5, -4.0, 6.5)) key = bpy.context.object key.name = "large_softbox_key_light" key.data.energy = 650 key.data.size = 5.0

bpy.ops.object.light_add(type='POINT', location=(-4.0, 3.0, 3.5)) rim = bpy.context.object rim.name = "small_rim_light" rim.data.energy = 90

Camera

bpy.ops.object.camera_add(location=(6.8, -5.2, 3.4), rotation=(math.radians(62), 0, math.radians(49))) bpy.context.scene.camera = bpy.context.object

Render settings

bpy.context.scene.render.engine = 'CYCLES' bpy.context.scene.cycles.samples = 96 bpy.context.scene.render.resolution_x = 1600 bpy.context.scene.render.resolution_y = 1000

Ambient/world light

bpy.context.scene.world.color = (0.035, 0.04, 0.035)

Set origin view clipping convenience

for obj in bpy.context.scene.objects: obj.select_set(False)

Add bevel/weighted normals to all mesh objects where sensible

for obj in bpy.context.scene.objects: if obj.type == 'MESH': obj.select_set(True) bpy.context.view_layer.objects.active = obj try: bpy.ops.object.shade_smooth() except Exception: pass obj.select_set(False)

print("Done: visual non-functional 2S19 Msta-S style model created.")