95 lines
4.5 KiB
Python
95 lines
4.5 KiB
Python
"""Run in Blender after loading the original into the isolated Portfolio Evolution scene.
|
|
Original scene and other worktrees are never saved or overwritten.
|
|
"""
|
|
import bpy
|
|
from pathlib import Path
|
|
|
|
root_path = Path(__file__).resolve().parents[3]
|
|
scene = bpy.data.scenes['Portfolio Evolution']
|
|
bpy.context.window.scene = scene
|
|
car = next(o for o in scene.objects if o.name.split('.')[0] == 'chase-car')
|
|
materials = {m.name.split('.')[0]: m for o in scene.objects if o.type == 'MESH' for m in o.data.materials}
|
|
for name, color, metallic, roughness in [
|
|
('M_Body_Gunmetal', (0.14, 0.21, 0.28, 1), 0.78, 0.28),
|
|
('M_Body_Haunch', (0.19, 0.28, 0.34, 1), 0.8, 0.24),
|
|
('M_Wheel', (0.28, 0.34, 0.39, 1), 0.9, 0.23),
|
|
]:
|
|
bsdf = materials[name].node_tree.nodes.get('Principled BSDF')
|
|
bsdf.inputs['Base Color'].default_value = color
|
|
bsdf.inputs['Metallic'].default_value = metallic
|
|
bsdf.inputs['Roughness'].default_value = roughness
|
|
bsdf.inputs['Coat Weight'].default_value = 0.28 if 'Body' in name else 0
|
|
bsdf.inputs['Coat Roughness'].default_value = 0.23
|
|
|
|
# Recess the old projecting cylinders into the tire face.
|
|
for o in scene.objects:
|
|
stem = o.name.split('.')[0]
|
|
if '-rim-' in stem and '-ring-' not in stem:
|
|
o.scale.z *= 0.16
|
|
o.location.x = (1 if o.location.x > 0 else -1) * 1.145
|
|
if stem.startswith('diffuser-fin-'):
|
|
o.scale.z *= 0.65
|
|
|
|
def box(name, location, dimensions, material, bevel=0.012):
|
|
bpy.ops.mesh.primitive_cube_add(size=1, location=location)
|
|
obj = bpy.context.object
|
|
obj.name = name
|
|
obj.dimensions = dimensions
|
|
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
|
obj.parent = car
|
|
obj.data.materials.append(material)
|
|
mod = obj.modifiers.new('Machined edge', 'BEVEL')
|
|
mod.width = bevel
|
|
mod.segments = 3
|
|
obj.modifiers.new('Weighted normals', 'WEIGHTED_NORMAL')
|
|
return obj
|
|
|
|
# Six individually editable louvers, following the slope of the rear glass.
|
|
for index in range(6):
|
|
t = index / 5
|
|
y = 0.30 + t * 0.74
|
|
z = 1.245 - t * 0.275
|
|
box('evolution-louver-%02d' % index, (0, y, z), (1.25 + t * 0.18, 0.052, 0.026), materials['M_Lower_Graphite'], 0.008)
|
|
box('evolution-deck-lip', (0, 2.07, 0.89), (1.72, 0.095, 0.055), materials['M_Body_Haunch'], 0.018)
|
|
# Dark dividers articulate a continuous lamp without increasing emissive energy.
|
|
for x in [-0.54, -0.27, 0, 0.27, 0.54]:
|
|
box('evolution-lamp-divider', (x, 2.68, 0.694), (0.012, 0.012, 0.047), materials['M_Lower_Graphite'], 0.002)
|
|
|
|
# Write just this authored scene and its dependencies, not other open projects.
|
|
bpy.data.libraries.write(str(root_path / 'assets/blender/evolution/nightdrive-evolution.blend'), {scene}, fake_user=True, compress=True)
|
|
|
|
# A separate export scene combines geometry by material: eight runtime draw groups.
|
|
export_scene = bpy.data.scenes.new('Evolution Export')
|
|
bpy.context.window.scene = export_scene
|
|
root = bpy.data.objects.new('evolution-chase-car', None)
|
|
export_scene.collection.objects.link(root)
|
|
names = {'M_Body_Gunmetal': 'car-body', 'M_Body_Haunch': 'body-haunches', 'M_Glass_Smoke': 'rear-glass', 'M_Rear_Light': 'rear-light-bar', 'M_Cyan_Accent': 'car-underglow-source', 'M_Lower_Graphite': 'body-trim', 'M_Wheel': 'wheel-machining', 'M_Tire': 'tires'}
|
|
bpy.context.window.scene = scene
|
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
|
groups = {}
|
|
for obj in scene.objects:
|
|
if obj.type != 'MESH' or obj.name.startswith('LOOKDEV'):
|
|
continue
|
|
evaluated = obj.evaluated_get(depsgraph)
|
|
mesh = bpy.data.meshes.new_from_object(evaluated, depsgraph=depsgraph)
|
|
mesh.transform(obj.matrix_world)
|
|
clone = bpy.data.objects.new('export-' + obj.name, mesh)
|
|
export_scene.collection.objects.link(clone)
|
|
key = obj.data.materials[0].name.split('.')[0]
|
|
groups.setdefault(key, []).append(clone)
|
|
bpy.context.window.scene = export_scene
|
|
for key, objects in groups.items():
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
for obj in objects:
|
|
obj.select_set(True)
|
|
bpy.context.view_layer.objects.active = objects[0]
|
|
if len(objects) > 1:
|
|
bpy.ops.object.join()
|
|
joined = bpy.context.object
|
|
joined.name = 'evolution-' + names[key]
|
|
joined.parent = root
|
|
bpy.ops.object.select_all(action='SELECT')
|
|
bpy.ops.export_scene.gltf(filepath=str(root_path / 'src/assets/nightdrive-grand-tourer-evolution.glb'), export_format='GLB', use_selection=True, use_active_scene=True, export_apply=True, export_animations=False, export_cameras=False, export_lights=False)
|
|
print('Exported', len(groups), 'material groups')
|
|
bpy.context.window.scene = scene
|