Skip to content
This repository was archived by the owner on Apr 24, 2025. It is now read-only.

Commit 6a71ba8

Browse files
committed
store vertex offsets and normals of selected mesh objects per frame into image textures
1 parent ecfa7f1 commit 6a71ba8

1 file changed

Lines changed: 192 additions & 0 deletions

File tree

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
2+
bl_info = {
3+
"name": "Vertex Animation",
4+
"author": "ibra-kdbra",
5+
"version": (1, 0),
6+
"blender": (4, 0, 1),
7+
"location": "View3D > Sidebar > Unreal Tools Tab",
8+
"description": "A tool for storing per frame vertex data for use in a vertex shader.",
9+
"warning": "",
10+
"doc_url": "",
11+
"category": "Unreal Tools",
12+
}
13+
14+
15+
import bpy
16+
import bmesh
17+
18+
19+
def get_per_frame_mesh_data(context, data, objects):
20+
"""Return a list of combined mesh data per frame"""
21+
meshes = []
22+
for i in frame_range(context.scene):
23+
context.scene.frame_set(i)
24+
depsgraph = context.evaluated_depsgraph_get()
25+
bm = bmesh.new()
26+
for ob in objects:
27+
eval_object = ob.evaluated_get(depsgraph)
28+
me = data.meshes.new_from_object(eval_object)
29+
me.transform(ob.matrix_world)
30+
bm.from_mesh(me)
31+
data.meshes.remove(me)
32+
me = data.meshes.new("mesh")
33+
bm.normal_update()
34+
bm.to_mesh(me)
35+
bm.free()
36+
me.update()
37+
meshes.append(me)
38+
return meshes
39+
40+
41+
def create_export_mesh_object(context, data, me):
42+
"""Return a mesh object with correct UVs"""
43+
while len(me.uv_layers) < 2:
44+
me.uv_layers.new()
45+
uv_layer = me.uv_layers[1]
46+
uv_layer.name = "vertex_anim"
47+
for loop in me.loops:
48+
uv_layer.data[loop.index].uv = (
49+
(loop.vertex_index + 0.5)/len(me.vertices), 128/255
50+
)
51+
ob = data.objects.new("export_mesh", me)
52+
context.scene.collection.objects.link(ob)
53+
return ob
54+
55+
56+
def get_vertex_data(data, meshes):
57+
"""Return lists of vertex offsets and normals from a list of mesh data"""
58+
original = meshes[0].vertices
59+
offsets = []
60+
normals = []
61+
for me in reversed(meshes):
62+
for v in me.vertices:
63+
offset = v.co - original[v.index].co
64+
x, y, z = offset
65+
offsets.extend((x, -y, z, 1))
66+
x, y, z = v.normal
67+
normals.extend(((x + 1) * 0.5, (-y + 1) * 0.5, (z + 1) * 0.5, 1))
68+
if not me.users:
69+
data.meshes.remove(me)
70+
return offsets, normals
71+
72+
73+
def frame_range(scene):
74+
"""Return a range object with with scene's frame start, end, and step"""
75+
return range(scene.frame_start, scene.frame_end, scene.frame_step)
76+
77+
78+
def bake_vertex_data(data, offsets, normals, size):
79+
"""Stores vertex offsets and normals in seperate image textures"""
80+
width, height = size
81+
offset_texture = data.images.new(
82+
name="offsets",
83+
width=width,
84+
height=height,
85+
alpha=True,
86+
float_buffer=True
87+
)
88+
normal_texture = data.images.new(
89+
name="normals",
90+
width=width,
91+
height=height,
92+
alpha=True
93+
)
94+
offset_texture.pixels = offsets
95+
normal_texture.pixels = normals
96+
97+
98+
class OBJECT_OT_ProcessAnimMeshes(bpy.types.Operator):
99+
"""Store combined per frame vertex offsets and normals for all
100+
selected mesh objects into seperate image textures"""
101+
bl_idname = "object.process_anim_meshes"
102+
bl_label = "Process Anim Meshes"
103+
104+
@property
105+
def allowed_modifiers(self):
106+
return [
107+
'ARMATURE', 'CAST', 'CURVE', 'DISPLACE', 'HOOK',
108+
'LAPLACIANDEFORM', 'LATTICE', 'MESH_DEFORM',
109+
'SHRINKWRAP', 'SIMPLE_DEFORM', 'SMOOTH',
110+
'CORRECTIVE_SMOOTH', 'LAPLACIANSMOOTH',
111+
'SURFACE_DEFORM', 'WARP', 'WAVE',
112+
]
113+
114+
@classmethod
115+
def poll(cls, context):
116+
ob = context.active_object
117+
return ob and ob.type == 'MESH' and ob.mode == 'OBJECT'
118+
119+
def execute(self, context):
120+
units = context.scene.unit_settings
121+
data = bpy.data
122+
objects = [ob for ob in context.selected_objects if ob.type == 'MESH']
123+
vertex_count = sum([len(ob.data.vertices) for ob in objects])
124+
frame_count = len(frame_range(context.scene))
125+
for ob in objects:
126+
for mod in ob.modifiers:
127+
if mod.type not in self.allowed_modifiers:
128+
self.report(
129+
{'ERROR'},
130+
f"Objects with {mod.type.title()} modifiers are not allowed!"
131+
)
132+
return {'CANCELLED'}
133+
if units.system != 'METRIC' or round(units.scale_length, 2) != 0.01:
134+
self.report(
135+
{'ERROR'},
136+
"Scene Unit must be Metric with a Unit Scale of 0.01!"
137+
)
138+
return {'CANCELLED'}
139+
if vertex_count > 8192:
140+
self.report(
141+
{'ERROR'},
142+
f"Vertex count of {vertex_count :,}, execedes limit of 8,192!"
143+
)
144+
return {'CANCELLED'}
145+
if frame_count > 8192:
146+
self.report(
147+
{'ERROR'},
148+
f"Frame count of {frame_count :,}, execedes limit of 8,192!"
149+
)
150+
return {'CANCELLED'}
151+
meshes = get_per_frame_mesh_data(context, data, objects)
152+
export_mesh_data = meshes[0].copy()
153+
create_export_mesh_object(context, data, export_mesh_data)
154+
offsets, normals = get_vertex_data(data, meshes)
155+
texture_size = vertex_count, frame_count
156+
bake_vertex_data(data, offsets, normals, texture_size)
157+
return {'FINISHED'}
158+
159+
160+
class VIEW3D_PT_VertexAnimation(bpy.types.Panel):
161+
"""Creates a Panel in 3D Viewport"""
162+
bl_label = "Vertex Animation"
163+
bl_idname = "VIEW3D_PT_vertex_animation"
164+
bl_space_type = 'VIEW_3D'
165+
bl_region_type = 'UI'
166+
bl_category = "Unreal Tools"
167+
168+
def draw(self, context):
169+
layout = self.layout
170+
layout.use_property_split = True
171+
layout.use_property_decorate = False
172+
scene = context.scene
173+
col = layout.column(align=True)
174+
col.prop(scene, "frame_start", text="Frame Start")
175+
col.prop(scene, "frame_end", text="End")
176+
col.prop(scene, "frame_step", text="Step")
177+
row = layout.row()
178+
row.operator("object.process_anim_meshes")
179+
180+
181+
def register():
182+
bpy.utils.register_class(OBJECT_OT_ProcessAnimMeshes)
183+
bpy.utils.register_class(VIEW3D_PT_VertexAnimation)
184+
185+
186+
def unregister():
187+
bpy.utils.unregister_class(OBJECT_OT_ProcessAnimMeshes)
188+
bpy.utils.unregister_class(VIEW3D_PT_VertexAnimation)
189+
190+
191+
if __name__ == "__main__":
192+
register()

0 commit comments

Comments
 (0)