-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodel_validator.py
More file actions
283 lines (229 loc) · 9.7 KB
/
model_validator.py
File metadata and controls
283 lines (229 loc) · 9.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
import bpy
import bmesh
import gpu
from gpu_extras.batch import batch_for_shader
from .core import *
class ModelValidatorObject:
MESH_DATAS = ('verts', 'edges', 'faces')
GEO_CHECKER = ('non_manifold', 'triangles', 'ngons')
VERTS_CHECKER = ('n_poles', 'e_poles', 'more_poles', 'isolated_verts')
def __init__(self, obj):
self._object = obj
self._bm_object = None
self._verts = 0
self._edges = 0
self._faces = 0
self._tris = 0
self._triangles = Triangles(self)
self._ngons = Ngons(self)
self._non_manifold = NonManifold(self)
self._poles = Poles(self)
self._init_object()
def _init_object(self):
bm = self.set_bm_object()
self.update_datas(bm)
def set_bm_object(self):
me = self._object.data
if me.is_editmode:
self._bm_object = bmesh.from_edit_mesh(me)
else:
bm = bmesh.new()
bm.from_mesh(me)
self._bm_object = bm
return self._bm_object
def update_datas(self, bm):
for data in self.MESH_DATAS:
setattr(self, f"_{data}", len(getattr(bm, data)))
self._tris = len(bm.calc_loop_triangles())
model_validator = bpy.context.window_manager.model_validator_props
for check in self.GEO_CHECKER:
if getattr(model_validator, check):
exec(f"self._{check}.set_datas()")
if any(getattr(model_validator, check) for check in self.VERTS_CHECKER):
self._poles.set_datas()
@property
def bm_object(self):
if self._bm_object is None or not self._bm_object.is_valid:
self.update_bm_object()
return self._bm_object
def update_bm_object(self):
bm = self.set_bm_object()
self.update_datas(bm)
return bm
def is_updated_datas(self, bm):
return any([getattr(self, f"_{data}") != len(getattr(bm, data))
for data in self.MESH_DATAS])
class ModelValidatorGPU:
_handler = None
@classmethod
def setup_handler(cls):
cls._handler = bpy.types.SpaceView3D.draw_handler_add(
cls.draw, (), 'WINDOW', 'POST_VIEW'
)
@classmethod
def remove_handler(cls):
bpy.types.SpaceView3D.draw_handler_remove(cls._handler, 'WINDOW')
@staticmethod
def draw_edges(coords, line_width, color):
shader = gpu.shader.from_builtin('UNIFORM_COLOR') # Fixed shader type
batch = batch_for_shader(shader, 'LINES', {"pos": coords})
shader.bind()
shader.uniform_float("color", color)
gpu.state.blend_set("ALPHA")
gpu.state.line_width_set(line_width)
batch.draw(shader)
@staticmethod
def draw_faces(coords, indices, color):
shader = gpu.shader.from_builtin('UNIFORM_COLOR') # Fixed shader type
batch = batch_for_shader(shader, 'TRIS', {"pos": coords}, indices=indices)
shader.bind()
shader.uniform_float("color", color)
gpu.state.blend_set("ALPHA")
batch.draw(shader)
@staticmethod
def draw_points(coords, point_size, color):
shader = gpu.shader.from_builtin('UNIFORM_COLOR') # Fixed shader type
batch = batch_for_shader(shader, 'POINTS', {"pos": coords})
shader.bind()
shader.uniform_float("color", color)
gpu.state.point_size_set(point_size)
batch.draw(shader)
@classmethod
def draw(cls):
context = bpy.context
if context.object is not None:
if not bpy.context.space_data.shading.show_xray:
gpu.state.depth_test_set('LESS')
model_validator = context.window_manager.model_validator_props
addon_prefs = context.preferences.addons[
__name__.split(".")[0]].preferences
for check in model_validator.checker_options:
if getattr(model_validator, check):
for mc_object in ModelValidator.objects.values():
if check in ('non_manifold', 'triangles', 'ngons'):
edges_offset = getattr(addon_prefs, 'edges_offset')
coords = getattr(mc_object,
f"_{check}").get_edges(edges_offset)
ModelValidatorGPU.draw_edges(
coords,
addon_prefs.line_width,
getattr(addon_prefs, f"{check}_color")
)
if check in ('triangles', 'ngons'):
face_offset = getattr(addon_prefs, 'edges_offset')
coords, indices = getattr(mc_object,
f"_{check}").get_faces(face_offset)
ModelValidatorGPU.draw_faces(
coords,
indices,
getattr(addon_prefs, f"{check}_color")
)
if check in ('n_poles', 'e_poles', 'more_poles',
'isolated_verts'):
point_offset = getattr(addon_prefs,
'points_offset')
coords = mc_object._poles.get_poles(
point_offset,
check
)
ModelValidatorGPU.draw_points(
coords,
addon_prefs.point_size,
getattr(addon_prefs, f"{check}_color")
)
gpu.state.depth_test_set('NONE')
class ModelValidator:
_mode = ""
objects = {}
@staticmethod
def poll():
model_validator = bpy.context.window_manager.model_validator_props
props = ("non_manifold", "triangles", "ngons",
"n_poles", "e_poles", "more_poles", "isolated_verts")
return model_validator.check_data and \
any([getattr(model_validator, prop) for prop in props])
@classmethod
def reset_model_validator(cls):
cls.set_mode("")
for mc_object in cls.objects.values():
del mc_object
cls.objects.clear()
@classmethod
def mode(cls):
return cls._mode
@classmethod
def set_mode(cls, states):
cls._mode = states
@classmethod
def add_model_validator_object(cls):
for obj in bpy.context.selected_objects:
if obj.type != "MESH" or cls.objects.get(obj):
continue
cls.objects[obj] = ModelValidatorObject(obj)
@classmethod
def remove_model_validator_object(cls, obj):
mc_object = cls.objects.get(obj)
if mc_object:
del mc_object
del cls.objects[obj]
@classmethod
def reset_mc_objects(cls):
for mc_object in cls.objects.values():
del mc_object
cls.objects.clear()
cls.add_model_validator_object()
@classmethod
def add_callback(cls):
if cls.callback not in bpy.app.handlers.depsgraph_update_post:
cls.add_model_validator_object()
bpy.app.handlers.depsgraph_update_post.append(cls.callback)
@classmethod
def remove_callback(cls):
if cls.callback in bpy.app.handlers.depsgraph_update_post:
bpy.app.handlers.depsgraph_update_post.remove(cls.callback)
cls.reset_model_validator()
@classmethod
def update_mc_object_datas(cls, checker_type):
"""
:param checker_type: string,
:return:
"""
if checker_type in {'e_poles', 'n_poles', 'more_poles',
'isolated_verts'}:
for mc_object in cls.objects.values():
mc_object._poles.set_datas()
else:
for mc_object in cls.objects.values():
getattr(mc_object, f"_{checker_type}").set_datas()
@staticmethod
def callback(scene):
"""
Before doing anything, we check that the mode haven't changed.
If this is the case and we are in EDIT mode, we check the validity
of registered ModelValidatorObject instances. For each instance,
we update its bmesh representation.
"""
if bpy.context.object is not None:
object_mode = bpy.context.object.mode
if object_mode != ModelValidator.mode():
ModelValidator.set_mode(object_mode)
ModelValidator.reset_mc_objects()
if object_mode == "OBJECT":
for obj in bpy.context.selected_objects:
if not ModelValidator.objects.get(obj):
ModelValidator.add_model_validator_object()
mc_objects = list(ModelValidator.objects.keys())
for obj in mc_objects:
if obj not in list(bpy.data.objects) or not obj.select_get():
ModelValidator.remove_model_validator_object(obj)
if object_mode == "EDIT" and ModelValidator.poll():
depsgraph = bpy.context.evaluated_depsgraph_get()
for obj, mc_object in ModelValidator.objects.items():
bm = mc_object.bm_object
for update in depsgraph.updates:
if update.id.original == obj and \
mc_object.is_updated_datas(bm):
mc_object.update_datas(bm)
else:
model_validator = bpy.context.window_manager.model_validator_props
model_validator.check_data = False