1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
20 from blenderkit
import utils
23 from object_print3d_utils
import operators
as ops
25 RENDER_OBTYPES
= ['MESH', 'CURVE', 'SURFACE', 'METABALL', 'TEXT']
28 def check_material(props
, mat
):
29 e
= bpy
.context
.scene
.render
.engine
32 props
.texture_count
= 0
34 props
.total_megapixels
= 0
35 props
.is_procedural
= True
39 if mat
.node_tree
is not None:
40 checknodes
= mat
.node_tree
.nodes
[:]
41 while len(checknodes
) > 0:
44 if n
.type == 'GROUP': # dive deeper here.
45 checknodes
.extend(n
.node_tree
.nodes
)
46 if len(n
.outputs
) == 1 and n
.outputs
[0].type == 'SHADER' and n
.type != 'GROUP':
47 if n
.type not in shaders
:
48 shaders
.append(n
.type)
49 if n
.type == 'TEX_IMAGE':
51 if n
.image
is not None:
52 mattype
= 'image based'
53 props
.is_procedural
= False
54 if n
.image
not in textures
:
55 textures
.append(n
.image
)
56 props
.texture_count
+= 1
57 props
.total_megapixels
+= (n
.image
.size
[0] * n
.image
.size
[1])
59 maxres
= max(n
.image
.size
[0], n
.image
.size
[1])
60 props
.texture_resolution_max
= max(props
.texture_resolution_max
, maxres
)
61 minres
= min(n
.image
.size
[0], n
.image
.size
[1])
62 if props
.texture_resolution_min
== 0:
63 props
.texture_resolution_min
= minres
65 props
.texture_resolution_min
= min(props
.texture_resolution_min
, minres
)
69 if s
.startswith('BSDF_'):
71 s
= s
.lower().replace('_', ' ')
72 props
.shaders
+= (s
+ ', ')
75 def check_render_engine(props
, obs
):
79 e
= bpy
.context
.scene
.render
.engine
85 props
.texture_count
= 0
86 props
.total_megapixels
= 0
88 for ob
in obs
: # TODO , this is duplicated here for other engines, otherwise this should be more clever.
89 for ms
in ob
.material_slots
:
90 if ms
.material
is not None:
92 if m
.name
not in materials
:
93 materials
.append(m
.name
)
94 if ob
.type == 'MESH' and len(ob
.data
.uv_layers
) > 0:
97 if e
== 'BLENDER_RENDER':
98 props
.engine
= 'BLENDER_INTERNAL'
101 props
.engine
= 'CYCLES'
103 for mname
in materials
:
104 m
= bpy
.data
.materials
[mname
]
105 if m
is not None and m
.node_tree
is not None:
106 checknodes
= m
.node_tree
.nodes
[:]
107 while len(checknodes
) > 0:
110 if n
.type == 'GROUP': # dive deeper here.
111 checknodes
.extend(n
.node_tree
.nodes
)
112 if len(n
.outputs
) == 1 and n
.outputs
[0].type == 'SHADER' and n
.type != 'GROUP':
113 if n
.type not in shaders
:
114 shaders
.append(n
.type)
115 if n
.type == 'TEX_IMAGE':
118 if n
.image
is not None and n
.image
not in textures
:
119 props
.is_procedural
= False
120 mattype
= 'image based'
122 textures
.append(n
.image
)
123 props
.texture_count
+= 1
124 props
.total_megapixels
+= (n
.image
.size
[0] * n
.image
.size
[1])
126 maxres
= max(n
.image
.size
[0], n
.image
.size
[1])
127 props
.texture_resolution_max
= max(props
.texture_resolution_max
, maxres
)
128 minres
= min(n
.image
.size
[0], n
.image
.size
[1])
129 if props
.texture_resolution_min
== 0:
130 props
.texture_resolution_min
= minres
132 props
.texture_resolution_min
= min(props
.texture_resolution_min
, minres
)
135 # if mattype == None:
136 # mattype = 'procedural'
137 # tags['material type'] = mattype
139 elif e
== 'BLENDER_GAME':
140 props
.engine
= 'BLENDER_GAME'
142 # write to object properties.
146 props
.materials
+= (m
+ ', ')
148 if s
.startswith('BSDF_'):
151 s
= s
.replace('_', ' ')
152 props
.shaders
+= (s
+ ', ')
155 def check_printable(props
, obs
):
158 ops
.Print3DCheckSolid
,
159 ops
.Print3DCheckIntersections
,
160 ops
.Print3DCheckDegenerate
,
161 ops
.Print3DCheckDistorted
,
162 ops
.Print3DCheckThick
,
163 ops
.Print3DCheckSharp
,
164 # ops.Print3DCheckOverhang,
170 for cls
in check_cls
:
171 cls
.main_check(ob
, info
)
175 passed
= item
[0].endswith(' 0')
180 props
.printable_3d
= printable
183 def check_rig(props
, obs
):
185 if ob
.type == 'ARMATURE':
189 def check_anim(props
, obs
):
192 if ob
.animation_data
is not None:
193 a
= ob
.animation_data
.action
196 if len(c
.keyframe_points
) > 1:
199 # c.keyframe_points.remove(c.keyframe_points[0])
201 props
.animated
= True
204 def check_meshprops(props
, obs
):
205 ''' checks polycount, manifold, mesh parts (not implemented)'''
217 if ob
.type == 'MESH' or ob
.type == 'CURVE':
219 if ob
.type == 'CURVE':
220 # depsgraph = bpy.context.evaluated_depsgraph_get()
221 # object_eval = ob.evaluated_get(depsgraph)
225 fco
= len(mesh
.polygons
)
227 vc
+= len(mesh
.vertices
)
229 for f
in mesh
.polygons
:
231 if len(f
.vertices
) == 3:
233 elif len(f
.vertices
) == 4:
235 elif len(f
.vertices
) > 4:
239 for i
, v
in enumerate(f
.vertices
):
240 v1
= f
.vertices
[i
- 1]
241 e
= (min(v
, v1
), max(v
, v1
))
242 edges_counts
[e
] = edges_counts
.get(e
, 0) + 1
244 # all meshes have to be manifold for this to work.
245 manifold
= manifold
and not any(i
in edges_counts
.values() for i
in [0, 1, 3, 4])
247 for m
in ob
.modifiers
:
248 if m
.type == 'SUBSURF' or m
.type == 'MULTIRES':
249 fcor
*= 4 ** m
.render_levels
250 if m
.type == 'SOLIDIFY': # this is rough estimate, not to waste time with evaluating all nonmanifold edges
252 if m
.type == 'ARRAY':
254 if m
.type == 'MIRROR':
256 if m
.type == 'DECIMATE':
261 ob_eval
.to_mesh_clear()
264 props
.face_count
= fc
265 props
.face_count_render
= fcr
266 # print(tris, quads, ngons)
267 if quads
> 0 and tris
== 0 and ngons
== 0:
268 props
.mesh_poly_type
= 'QUAD'
269 elif quads
> tris
and quads
> ngons
:
270 props
.mesh_poly_type
= 'QUAD_DOMINANT'
271 elif tris
> quads
and tris
> quads
:
272 props
.mesh_poly_type
= 'TRI_DOMINANT'
273 elif quads
== 0 and tris
> 0 and ngons
== 0:
274 props
.mesh_poly_type
= 'TRI'
275 elif ngons
> quads
and ngons
> tris
:
276 props
.mesh_poly_type
= 'NGON'
278 props
.mesh_poly_type
= 'OTHER'
280 props
.manifold
= manifold
283 def countObs(props
, obs
):
287 otype
= ob
.type.lower()
288 ob_types
[otype
] = ob_types
.get(otype
, 0) + 1
289 props
.object_count
= count
292 def check_modifiers(props
, obs
):
297 for m
in ob
.modifiers
:
299 mtype
= mtype
.replace('_', ' ')
300 mtype
= mtype
.lower()
301 # mtype = mtype.capitalize()
302 if mtype
not in modifiers
:
303 modifiers
.append(mtype
)
304 if m
.type == 'SMOKE':
305 if m
.smoke_type
== 'FLOW':
306 smt
= m
.flow_settings
.smoke_flow_type
307 if smt
== 'BOTH' or smt
== 'FIRE':
308 modifiers
.append('fire')
310 # for mt in modifiers:
311 effectmodifiers
= ['soft body', 'fluid simulation', 'particle system', 'collision', 'smoke', 'cloth',
314 if m
in effectmodifiers
:
315 props
.simulation
= True
316 if ob
.rigid_body
is not None:
317 props
.simulation
= True
318 modifiers
.append('rigid body')
323 props
.modifiers
= finalstr
327 """ call all analysis functions """
328 ui
= bpy
.context
.scene
.blenderkitUI
329 if ui
.asset_type
== 'MODEL':
330 ob
= utils
.get_active_model()
331 obs
= utils
.get_hierarchy(ob
)
332 props
= ob
.blenderkit
336 # reset some properties here, because they might not get re-filled at all when they aren't needed anymore.
337 props
.texture_resolution_max
= 0
338 props
.texture_resolution_min
= 0
339 # disabled printing checking, some 3d print addon bug.
340 # check_printable( props, obs)
341 check_render_engine(props
, obs
)
343 dim
, bbox_min
, bbox_max
= utils
.get_dimensions(obs
)
344 props
.dimensions
= dim
345 props
.bbox_min
= bbox_min
346 props
.bbox_max
= bbox_max
348 check_rig(props
, obs
)
349 check_anim(props
, obs
)
350 check_meshprops(props
, obs
)
351 check_modifiers(props
, obs
)
353 elif ui
.asset_type
== 'MATERIAL':
354 # reset some properties here, because they might not get re-filled at all when they aren't needed anymore.
356 mat
= utils
.get_active_asset()
357 props
= mat
.blenderkit
358 props
.texture_resolution_max
= 0
359 props
.texture_resolution_min
= 0
360 check_material(props
, mat
)
361 elif ui
.asset_type
== 'HDR':
362 # reset some properties here, because they might not get re-filled at all when they aren't needed anymore.
364 hdr
= utils
.get_active_asset()
365 props
= hdr
.blenderkit
366 props
.texture_resolution_max
= max(hdr
.size
[0],hdr
.size
[1])
369 class AutoFillTags(bpy
.types
.Operator
):
370 """Fill tags for asset. Now run before upload, no need to interact from user side"""
371 bl_idname
= "object.blenderkit_auto_tags"
372 bl_label
= "Generate Auto Tags for BlenderKit"
373 bl_options
= {'REGISTER', 'UNDO', 'INTERNAL'}
376 def poll(cls
, context
):
377 return utils
.uploadable_asset_poll()
379 def execute(self
, context
):
384 def register_asset_inspector():
385 bpy
.utils
.register_class(AutoFillTags
)
388 def unregister_asset_inspector():
389 bpy
.utils
.unregister_class(AutoFillTags
)
392 if __name__
== "__main__":