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 paths
, utils
, bg_blender
, ui_panels
22 import tempfile
, os
, subprocess
, json
, sys
26 BLENDERKIT_EXPORT_DATA_FILE
= "data.json"
29 def check_thumbnail(props
, imgpath
):
30 img
= utils
.get_hidden_image(imgpath
, 'upload_preview', force_reload
=True)
31 if img
is not None: # and img.size[0] == img.size[1] and img.size[0] >= 512 and (
32 # img.file_format == 'JPEG' or img.file_format == 'PNG'):
33 props
.has_thumbnail
= True
34 props
.thumbnail_generating_state
= ''
37 props
.has_thumbnail
= False
39 if img
is None or img
.size
[0] == 0 or img
.filepath
.find('thumbnail_notready.jpg') > -1:
40 output
+= 'No thumbnail or wrong file path\n'
43 # this is causing problems on some platforms, don't know why..
44 # if img.size[0] != img.size[1]:
45 # output += 'image not a square\n'
46 # if img.size[0] < 512:
47 # output += 'image too small, should be at least 512x512\n'
48 # if img.file_format != 'JPEG' or img.file_format != 'PNG':
49 # output += 'image has to be a jpeg or png'
50 props
.thumbnail_generating_state
= output
53 def update_upload_model_preview(self
, context
):
54 ob
= utils
.get_active_model()
57 imgpath
= props
.thumbnail
58 check_thumbnail(props
, imgpath
)
61 def update_upload_scene_preview(self
, context
):
64 imgpath
= props
.thumbnail
65 check_thumbnail(props
, imgpath
)
68 def update_upload_material_preview(self
, context
):
69 if hasattr(bpy
.context
, 'active_object') \
70 and bpy
.context
.view_layer
.objects
.active
is not None \
71 and bpy
.context
.active_object
.active_material
is not None:
72 mat
= bpy
.context
.active_object
.active_material
73 props
= mat
.blenderkit
74 imgpath
= props
.thumbnail
75 check_thumbnail(props
, imgpath
)
78 def update_upload_brush_preview(self
, context
):
79 brush
= utils
.get_active_brush()
81 props
= brush
.blenderkit
82 imgpath
= bpy
.path
.abspath(brush
.icon_filepath
)
83 check_thumbnail(props
, imgpath
)
86 def start_thumbnailer(self
, context
):
87 # Prepare to save the file
88 mainmodel
= utils
.get_active_model()
89 mainmodel
.blenderkit
.is_generating_thumbnail
= True
90 mainmodel
.blenderkit
.thumbnail_generating_state
= 'starting blender instance'
92 binary_path
= bpy
.app
.binary_path
93 script_path
= os
.path
.dirname(os
.path
.realpath(__file__
))
94 basename
, ext
= os
.path
.splitext(bpy
.data
.filepath
)
96 basename
= os
.path
.join(basename
, "temp")
99 asset_name
= mainmodel
.name
100 tempdir
= tempfile
.mkdtemp()
102 file_dir
= os
.path
.dirname(bpy
.data
.filepath
)
103 thumb_path
= os
.path
.join(file_dir
, asset_name
)
104 rel_thumb_path
= os
.path
.join('//', asset_name
)
107 while os
.path
.isfile(thumb_path
+ '.jpg'):
108 thumb_path
= os
.path
.join(file_dir
, asset_name
+ '_' + str(i
).zfill(4))
109 rel_thumb_path
= os
.path
.join('//', asset_name
+ '_' + str(i
).zfill(4))
112 filepath
= os
.path
.join(tempdir
, "thumbnailer_blenderkit" + ext
)
113 tfpath
= paths
.get_thumbnailer_filepath()
114 datafile
= os
.path
.join(tempdir
, BLENDERKIT_EXPORT_DATA_FILE
)
116 # save a copy of actual scene but don't interfere with the users models
117 bpy
.ops
.wm
.save_as_mainfile(filepath
=filepath
, compress
=False, copy
=True)
119 obs
= utils
.get_hierarchy(mainmodel
)
122 obnames
.append(ob
.name
)
123 with
open(datafile
, 'w', encoding
= 'utf-8') as s
:
124 bkit
= mainmodel
.blenderkit
127 "models": str(obnames
),
128 "thumbnail_angle": bkit
.thumbnail_angle
,
129 "thumbnail_snap_to": bkit
.thumbnail_snap_to
,
130 "thumbnail_background_lightness": bkit
.thumbnail_background_lightness
,
131 "thumbnail_resolution": bkit
.thumbnail_resolution
,
132 "thumbnail_samples": bkit
.thumbnail_samples
,
133 "thumbnail_denoising": bkit
.thumbnail_denoising
,
134 }, s
, ensure_ascii
=False, indent
=4)
136 proc
= subprocess
.Popen([
141 "--python", os
.path
.join(script_path
, "autothumb_model_bg.py"),
142 "--", datafile
, filepath
, thumb_path
, tempdir
143 ], bufsize
=1, stdout
=subprocess
.PIPE
, stdin
=subprocess
.PIPE
, creationflags
=utils
.get_process_flags())
145 eval_path_computing
= "bpy.data.objects['%s'].blenderkit.is_generating_thumbnail" % mainmodel
.name
146 eval_path_state
= "bpy.data.objects['%s'].blenderkit.thumbnail_generating_state" % mainmodel
.name
147 eval_path
= "bpy.data.objects['%s']" % mainmodel
.name
149 bg_blender
.add_bg_process(eval_path_computing
=eval_path_computing
, eval_path_state
=eval_path_state
,
150 eval_path
=eval_path
, process_type
='THUMBNAILER', process
=proc
)
152 mainmodel
.blenderkit
.thumbnail
= rel_thumb_path
+ '.jpg'
153 mainmodel
.blenderkit
.thumbnail_generating_state
= 'Saving .blend file'
155 except Exception as e
:
156 self
.report({'WARNING'}, "Error while exporting file: %s" % str(e
))
160 def start_material_thumbnailer(self
, context
, wait
=False):
161 # Prepare to save the file
162 mat
= bpy
.context
.active_object
.active_material
163 mat
.blenderkit
.is_generating_thumbnail
= True
164 mat
.blenderkit
.thumbnail_generating_state
= 'starting blender instance'
166 binary_path
= bpy
.app
.binary_path
167 script_path
= os
.path
.dirname(os
.path
.realpath(__file__
))
168 basename
, ext
= os
.path
.splitext(bpy
.data
.filepath
)
170 basename
= os
.path
.join(basename
, "temp")
173 asset_name
= mat
.name
174 tempdir
= tempfile
.mkdtemp()
176 file_dir
= os
.path
.dirname(bpy
.data
.filepath
)
178 thumb_path
= os
.path
.join(file_dir
, asset_name
)
179 rel_thumb_path
= os
.path
.join('//', mat
.name
)
181 while os
.path
.isfile(thumb_path
+ '.png'):
182 thumb_path
= os
.path
.join(file_dir
, mat
.name
+ '_' + str(i
).zfill(4))
183 rel_thumb_path
= os
.path
.join('//', mat
.name
+ '_' + str(i
).zfill(4))
186 filepath
= os
.path
.join(tempdir
, "material_thumbnailer_cycles" + ext
)
187 tfpath
= paths
.get_material_thumbnailer_filepath()
188 datafile
= os
.path
.join(tempdir
, BLENDERKIT_EXPORT_DATA_FILE
)
190 # save a copy of actual scene but don't interfere with the users models
191 bpy
.ops
.wm
.save_as_mainfile(filepath
=filepath
, compress
=False, copy
=True)
193 with
open(datafile
, 'w', encoding
= 'utf-8') as s
:
194 bkit
= mat
.blenderkit
197 "material": mat
.name
,
198 "thumbnail_type": bkit
.thumbnail_generator_type
,
199 "thumbnail_scale": bkit
.thumbnail_scale
,
200 "thumbnail_background": bkit
.thumbnail_background
,
201 "thumbnail_background_lightness": bkit
.thumbnail_background_lightness
,
202 "thumbnail_resolution": bkit
.thumbnail_resolution
,
203 "thumbnail_samples": bkit
.thumbnail_samples
,
204 "thumbnail_denoising": bkit
.thumbnail_denoising
,
205 "adaptive_subdivision": bkit
.adaptive_subdivision
,
206 "texture_size_meters": bkit
.texture_size_meters
,
207 }, s
, ensure_ascii
=False, indent
=4)
209 proc
= subprocess
.Popen([
214 "--python", os
.path
.join(script_path
, "autothumb_material_bg.py"),
215 "--", datafile
, filepath
, thumb_path
, tempdir
216 ], bufsize
=1, stdout
=subprocess
.PIPE
, stdin
=subprocess
.PIPE
, creationflags
=utils
.get_process_flags())
218 eval_path_computing
= "bpy.data.materials['%s'].blenderkit.is_generating_thumbnail" % mat
.name
219 eval_path_state
= "bpy.data.materials['%s'].blenderkit.thumbnail_generating_state" % mat
.name
220 eval_path
= "bpy.data.materials['%s']" % mat
.name
222 bg_blender
.add_bg_process(eval_path_computing
=eval_path_computing
, eval_path_state
=eval_path_state
,
223 eval_path
=eval_path
, process_type
='THUMBNAILER', process
=proc
)
225 mat
.blenderkit
.thumbnail
= rel_thumb_path
+ '.png'
226 mat
.blenderkit
.thumbnail_generating_state
= 'Saving .blend file'
227 except Exception as e
:
228 self
.report({'WARNING'}, "Error while packing file: %s" % str(e
))
232 class GenerateThumbnailOperator(bpy
.types
.Operator
):
233 """Generate Cycles thumbnail for model assets"""
234 bl_idname
= "object.blenderkit_generate_thumbnail"
235 bl_label
= "BlenderKit Thumbnail Generator"
236 bl_options
= {'REGISTER', 'INTERNAL'}
239 def poll(cls
, context
):
240 return bpy
.context
.view_layer
.objects
.active
is not None
242 def draw(self
, context
):
243 ob
= bpy
.context
.active_object
244 while ob
.parent
is not None:
246 props
= ob
.blenderkit
248 layout
.label(text
='thumbnailer settings')
249 layout
.prop(props
, 'thumbnail_background_lightness')
250 layout
.prop(props
, 'thumbnail_angle')
251 layout
.prop(props
, 'thumbnail_snap_to')
252 layout
.prop(props
, 'thumbnail_samples')
253 layout
.prop(props
, 'thumbnail_resolution')
254 layout
.prop(props
, 'thumbnail_denoising')
255 preferences
= bpy
.context
.preferences
.addons
['blenderkit'].preferences
256 layout
.prop(preferences
, "thumbnail_use_gpu")
258 def execute(self
, context
):
259 start_thumbnailer(self
, context
)
262 def invoke(self
, context
, event
):
263 wm
= context
.window_manager
264 if bpy
.data
.filepath
== '':
265 ui_panels
.ui_message(
266 title
= "Can't render thumbnail",
267 message
= "please save your file first")
271 return wm
.invoke_props_dialog(self
)
274 class GenerateMaterialThumbnailOperator(bpy
.types
.Operator
):
276 bl_idname
= "object.blenderkit_material_thumbnail"
277 bl_label
= "BlenderKit Material Thumbnail Generator"
278 bl_options
= {'REGISTER', 'INTERNAL'}
281 def poll(cls
, context
):
282 return bpy
.context
.view_layer
.objects
.active
is not None
284 def check(self
, context
):
287 def draw(self
, context
):
289 props
= bpy
.context
.active_object
.active_material
.blenderkit
290 layout
.prop(props
, 'thumbnail_generator_type')
291 layout
.prop(props
, 'thumbnail_scale')
292 layout
.prop(props
, 'thumbnail_background')
293 if props
.thumbnail_background
:
294 layout
.prop(props
, 'thumbnail_background_lightness')
295 layout
.prop(props
, 'thumbnail_resolution')
296 layout
.prop(props
, 'thumbnail_samples')
297 layout
.prop(props
, 'thumbnail_denoising')
298 layout
.prop(props
, 'adaptive_subdivision')
299 preferences
= bpy
.context
.preferences
.addons
['blenderkit'].preferences
300 layout
.prop(preferences
, "thumbnail_use_gpu")
302 def execute(self
, context
):
303 start_material_thumbnailer(self
, context
)
307 def invoke(self
, context
, event
):
308 wm
= context
.window_manager
309 return wm
.invoke_props_dialog(self
)
312 def register_thumbnailer():
313 bpy
.utils
.register_class(GenerateThumbnailOperator
)
314 bpy
.utils
.register_class(GenerateMaterialThumbnailOperator
)
317 def unregister_thumbnailer():
318 bpy
.utils
.unregister_class(GenerateThumbnailOperator
)
319 bpy
.utils
.unregister_class(GenerateMaterialThumbnailOperator
)