import os import sys import bpy def link_object(name, data): obj = bpy.data.objects.new(name, data) bpy.context.scene.collection.objects.link(obj) return obj def build_curve(): data = bpy.data.curves.new("WebCurveData", "CURVE") data.dimensions = "3D" data.resolution_u = 8 poly = data.splines.new("POLY") poly.points.add(3) for point, co in zip(poly.points, ((-2.0, 0.0, 0.0, 1.0), (-0.8, 0.7, 0.2, 1.0), (0.6, -0.4, 0.4, 1.0), (2.0, 0.2, 0.0, 1.0))): point.co = co bezier = data.splines.new("BEZIER") bezier.bezier_points.add(2) for point, co in zip(bezier.bezier_points, ((-1.5, -1.0, 0.0), (0.0, -1.8, 0.5), (1.5, -1.0, 0.0))): point.co = co point.handle_left_type = "AUTO" point.handle_right_type = "AUTO" return link_object("WebCurveObject", data) def build_surface(): bpy.ops.surface.primitive_nurbs_surface_surface_add(enter_editmode=False) obj = bpy.context.object obj.name = "WebSurfaceObject" data = obj.data data.name = "WebSurfaceData" data.resolution_u = 4 data.resolution_v = 4 spline = data.splines[0] spline.order_u = 4 spline.order_v = 4 spline.use_endpoint_u = True spline.use_endpoint_v = True for index, point in enumerate(spline.points): u = index % 4 v = index // 4 x = -1.0 + 2.0 * u / 3.0 y = v / 3.0 z = 0.35 * (1.0 if u in (1, 2) else 0.0) * (1.0 if v in (1, 2) else 0.0) point.co = (x, y, z, 1.0) return obj def build_text(): data = bpy.data.curves.new("WebFontData", "FONT") data.body = "Web Blender" data.align_x = "CENTER" data.size = 0.7 root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) font_path = os.path.join(root, "blender-5.2.0", "release", "datafiles", "bfont.pfb") regular = bpy.data.fonts.load(font_path, check_existing=False) alternate = bpy.data.fonts.load(font_path, check_existing=False) regular.name = "WebFontRegular" alternate.name = "WebFontAlternate" data.font = regular data.font_bold = alternate data.font_italic = regular data.font_bold_italic = alternate return link_object("WebFontObject", data) def build_metaball(): data = bpy.data.metaballs.new("WebMetaballData") data.resolution = 0.2 first = data.elements.new() first.co = (-0.65, 0.0, 0.4) first.radius = 0.75 second = data.elements.new() second.co = (0.65, 0.0, 0.4) second.radius = 0.55 return link_object("WebMetaballObject", data) def build_point_cloud(): mesh = bpy.data.meshes.new("WebPointSeed") mesh.from_pydata(((-1.5, 1.4, 0.0), (-0.5, 1.7, 0.2), (0.5, 1.4, 0.0), (1.5, 1.7, 0.2)), [], []) obj = link_object("WebPointCloudObject", mesh) bpy.ops.object.select_all(action="DESELECT") bpy.context.view_layer.objects.active = obj obj.select_set(True) bpy.ops.object.convert(target="POINTCLOUD") obj.data.name = "WebPointCloudData" weight = obj.data.attributes.new("web_weight", "FLOAT", "POINT") for index, item in enumerate(weight.data): item.value = 0.25 + index * 0.25 radius = obj.data.attributes.get("radius") or obj.data.attributes.new("radius", "FLOAT", "POINT") for index, item in enumerate(radius.data): item.value = 0.05 + index * 0.01 return obj def build_hair(): surface_mesh = bpy.data.meshes.new("WebHairSurfaceData") surface_mesh.from_pydata(((-2.0, 3.0, -0.1), (2.0, 3.0, -0.1), (2.0, 4.0, -0.1), (-2.0, 4.0, -0.1)), (), ((0, 1, 2, 3),)) surface = link_object("WebHairSurface", surface_mesh) surface.hide_viewport = True surface.hide_render = True legacy = bpy.data.curves.new("WebHairSeed", "CURVE") legacy.dimensions = "3D" spline = legacy.splines.new("POLY") spline.points.add(3) for point, co in zip(spline.points, ((-1.2, 3.2, 0.0, 1.0), (-0.5, 3.4, 0.5, 1.0), (0.4, 3.3, 0.8, 1.0), (1.2, 3.5, 1.0, 1.0))): point.co = co obj = link_object("WebHairObject", legacy) bpy.ops.object.select_all(action="DESELECT") bpy.context.view_layer.objects.active = obj obj.select_set(True) bpy.ops.object.convert(target="CURVES") if legacy != obj.data: bpy.data.curves.remove(legacy, do_unlink=True) obj.data.name = "WebHairData" obj.data.surface = surface radius = obj.data.attributes.get("radius") or obj.data.attributes.new("radius", "FLOAT", "POINT") for index, item in enumerate(radius.data): item.value = 0.02 + index * 0.004 density = obj.data.attributes.new("web_density", "FLOAT", "CURVE") density.data[0].value = 0.75 return obj def build_curves(): legacy = bpy.data.curves.new("WebCurvesSeed", "CURVE") legacy.dimensions = "3D" spline = legacy.splines.new("BEZIER") spline.bezier_points.add(3) for point, co in zip(spline.bezier_points, ((-1.5, 2.5, 0.0), (-0.5, 2.8, 0.3), (0.5, 2.5, 0.0), (1.5, 2.8, 0.3))): point.co = co point.handle_left_type = "AUTO" point.handle_right_type = "AUTO" obj = link_object("WebCurvesObject", legacy) bpy.ops.object.select_all(action="DESELECT") bpy.context.view_layer.objects.active = obj obj.select_set(True) bpy.ops.object.convert(target="CURVES") if legacy != obj.data: bpy.data.curves.remove(legacy, do_unlink=True) obj.data.name = "WebCurvesData" radius = obj.data.attributes.get("radius") or obj.data.attributes.new("radius", "FLOAT", "POINT") for index, item in enumerate(radius.data): item.value = 0.03 + index * 0.005 color = obj.data.attributes.new("web_color", "FLOAT_COLOR", "POINT") for index, item in enumerate(color.data): item.color = (index / 4.0, 0.4, 0.8, 1.0) return obj def build_volume(): data = bpy.data.volumes.new("WebVolumeData") data.filepath = "//missing_web_volume.vdb" return link_object("WebVolumeObject", data) def main(output_path): bpy.ops.wm.read_factory_settings(use_empty=True) build_curve() build_surface() build_text() build_metaball() build_point_cloud() build_curves() build_hair() bpy.ops.file.pack_all() build_volume() bpy.context.scene.frame_start = 1 bpy.context.scene.frame_end = 24 os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True) bpy.ops.wm.save_as_mainfile(filepath=os.path.abspath(output_path)) print(f"nonmesh-fixture-generated output={os.path.abspath(output_path)}") if __name__ == "__main__": if "--" not in sys.argv or len(sys.argv) <= sys.argv.index("--") + 1: raise SystemExit("usage: blender -b --python generate-nonmesh-fixture.py -- output.blend") main(sys.argv[sys.argv.index("--") + 1])