Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
"""Create the atlas's own solid mesh. Run with Blender --background --python.
|
||||
|
||||
Coordinates below are metres in Enfusion's X/right, Y/front, Z/up frame.
|
||||
The printable face retains the existing 744x1024 layout and its left gutter.
|
||||
The rigid book and separate turning sheet use the native atlas hand graph.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import math
|
||||
import bpy
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
OUT = ROOT / 'addons/core/Assets/Items/ERR_Atlas'
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
|
||||
def xyz(p):
|
||||
# Place the rigid paper just in front of the stock folded-map grip.
|
||||
return (p[0] + .015, -p[2], p[1] + .018)
|
||||
|
||||
materials = {}
|
||||
for name, color, metal in [('AtlasPrint', (.85,.8,.7,1), 0),
|
||||
('AtlasPaper', (.73,.70,.62,1), 0),
|
||||
('AtlasCover', (.48,.018,.012,1), 0),
|
||||
('AtlasWire', (.8,.8,.78,1), .65)]:
|
||||
mat = bpy.data.materials.new(name)
|
||||
mat.diffuse_color = color
|
||||
mat.use_nodes = True
|
||||
bsdf = mat.node_tree.nodes.get('Principled BSDF')
|
||||
bsdf.inputs['Base Color'].default_value = color
|
||||
bsdf.inputs['Metallic'].default_value = metal
|
||||
bsdf.inputs['Roughness'].default_value = .4 if metal else .8
|
||||
materials[name] = mat
|
||||
|
||||
def mesh(name, vertices, faces, material, uvs=None):
|
||||
data = bpy.data.meshes.new(name)
|
||||
data.from_pydata([xyz(v) for v in vertices], [], faces)
|
||||
data.update()
|
||||
obj = bpy.data.objects.new(name, data)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
data.materials.append(materials[material])
|
||||
uv = data.uv_layers.new(name='UVMap')
|
||||
for polygon in data.polygons:
|
||||
for loop in polygon.loop_indices:
|
||||
v = data.loops[loop].vertex_index
|
||||
uv.data[loop].uv = uvs[v] if uvs else (0,0)
|
||||
return obj
|
||||
|
||||
def box(name, lo, hi, material):
|
||||
x,y,z = lo; X,Y,Z = hi
|
||||
return mesh(name, [(x,y,z),(X,y,z),(X,Y,z),(x,Y,z),
|
||||
(x,y,Z),(X,y,Z),(X,Y,Z),(x,Y,Z)],
|
||||
[(0,3,2,1),(4,5,6,7),(0,1,5,4),(3,7,6,2),
|
||||
(0,4,7,3),(1,2,6,5)], material)
|
||||
|
||||
# The paper mesh used a folded sheet. This replacement has separate covers,
|
||||
# a 10 mm block of leaves and actual wire geometry with depth and silhouette.
|
||||
left, right, bottom, top = -.083, .108, -.1485, .1485
|
||||
box('BackCover', (left-.002,-.012,bottom-.002), (right+.002,-.010,top+.002), 'AtlasCover')
|
||||
box('PageBlock', (left,-.010,bottom), (right,-.0005,top), 'AtlasPaper')
|
||||
# Small real edge seams catch light at grazing angles without one mesh per leaf.
|
||||
for n in range(12):
|
||||
y = -.0096+n*.00075
|
||||
box('PageEdge_%02d'%n, (left,y,bottom-.00025), (right+.0003,y+.00016,top+.00025), 'AtlasPaper')
|
||||
box('FrontCover', (left-.002,-.0005,bottom-.002), (right+.002,.0005,top+.002), 'AtlasCover')
|
||||
mesh('PrintedSheet', [(left,.0006,bottom),(right,.0006,bottom),
|
||||
(right,.0006,top),(left,.0006,top)], [(0,3,2,1)], 'AtlasPrint',
|
||||
[(80/744,1),(1,1),(1,0),(80/744,0)])
|
||||
|
||||
# Continuous helix around the spine; the upper arc passes through the gutter
|
||||
# and the lower arc wraps behind the page block. No painted fake rings.
|
||||
vertices=[]; faces=[]
|
||||
turns=14; steps=turns*32; sides=6
|
||||
for i in range(steps+1):
|
||||
angle=i/32*2*math.pi
|
||||
z=bottom+.009+(top-bottom-.018)*i/steps
|
||||
for j in range(sides):
|
||||
tube=j/sides*2*math.pi
|
||||
radius=.0088+.0009*math.cos(tube)
|
||||
vertices.append((left+.002+radius*math.cos(angle),
|
||||
-.005+radius*math.sin(angle), z+.0009*math.sin(tube)))
|
||||
for i in range(steps):
|
||||
for j in range(sides):
|
||||
a=i*sides+j; b=i*sides+(j+1)%sides
|
||||
faces.append((a,b,b+sides,a+sides))
|
||||
wire=mesh('SpiralWire',vertices,faces,'AtlasWire')
|
||||
for poly in wire.data.polygons: poly.use_smooth=True
|
||||
|
||||
# A simple inventory collider; no dynamic page physics or runtime allocations.
|
||||
collider=box('UBX_Atlas', (left,-.013,bottom), (right,.004,top), 'AtlasPaper')
|
||||
collider['usage']='Geometry'
|
||||
collider.hide_render=True
|
||||
bpy.context.scene.unit_settings.system='METRIC'
|
||||
bpy.context.scene.unit_settings.scale_length=1
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
source = ROOT / 'source/atlas'
|
||||
source.mkdir(parents=True, exist_ok=True)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(source/'ERR_Atlas.blend'))
|
||||
bpy.ops.export_scene.fbx(filepath=str(OUT/'ERR_Atlas.fbx'), use_selection=True,
|
||||
object_types={'MESH'}, apply_unit_scale=True, apply_scale_options='FBX_SCALE_ALL',
|
||||
axis_forward='-Z', axis_up='Y', bake_space_transform=True,
|
||||
use_mesh_modifiers=True, use_custom_props=True, add_leaf_bones=False, bake_anim=False)
|
||||
print('ERR ATLAS MODEL EXPORTED', OUT)
|
||||
|
||||
# Separate lightweight turning leaf. Its origin is the spine hinge; runtime
|
||||
# rotates this visual only, keeping the held book and gripping hand steady.
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
verts=[]; faces=[]; uvs=[]
|
||||
for i in range(17):
|
||||
u=i/16
|
||||
for z,v in [(bottom,1),(top,0)]:
|
||||
# Subtract the book placement offset: this mesh is hinge-local.
|
||||
verts.append((.191*u-.015, .004*math.sin(math.pi*u)-.018, z))
|
||||
uvs.append((80/744+(1-80/744)*u,v))
|
||||
for i in range(16):
|
||||
faces.append((2*i,2*i+1,2*i+3,2*i+2))
|
||||
leaf=mesh('TurningLeaf',verts,faces,'AtlasPrint',uvs)
|
||||
back=mesh('LeafBack',verts,[tuple(reversed(f)) for f in faces],'AtlasPaper',uvs)
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(source/'ERR_AtlasLeaf.blend'))
|
||||
bpy.ops.export_scene.fbx(filepath=str(OUT/'ERR_AtlasLeaf.fbx'),use_selection=True,
|
||||
object_types={'MESH'},apply_unit_scale=True,apply_scale_options='FBX_SCALE_ALL',
|
||||
axis_forward='-Z',axis_up='Y',bake_space_transform=True,
|
||||
use_mesh_modifiers=True,use_custom_props=True,add_leaf_bones=False,bake_anim=False)
|
||||
Reference in New Issue
Block a user