Fix #104999: Node Wrangler: Principled Textures Setup emissive texture
[blender-addons.git] / space_view3d_stored_views / core.py
blob0476450d951aeb73e84af39d97c2eb7c798a129a
1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Authors: nfloyd, Francesco Siddi
7 import logging
8 module_logger = logging.getLogger(__name__)
10 import hashlib
11 import bpy
14 #Utility function get preferences setting for exporters
15 def get_preferences():
16 # replace the key if the add-on name changes
17 addon = bpy.context.preferences.addons[__package__]
18 show_warn = (addon.preferences.show_exporters if addon else False)
20 return show_warn
23 class StoredView():
24 def __init__(self, mode, index=None):
25 self.logger = logging.getLogger('%s.StoredView' % __name__)
26 self.scene = bpy.context.scene
27 self.view3d = bpy.context.space_data
28 self.index = index
29 self.data_store = DataStore(mode=mode)
31 def save(self):
32 if self.index == -1:
33 stored_view, self.index = self.data_store.create()
34 else:
35 stored_view = self.data_store.get(self.index)
36 self.from_v3d(stored_view)
37 self.logger.debug('index: %s name: %s' % (self.data_store.current_index, stored_view.name))
39 def set(self):
40 stored_view = self.data_store.get(self.index)
41 self.update_v3d(stored_view)
42 self.logger.debug('index: %s name: %s' % (self.data_store.current_index, stored_view.name))
44 def from_v3d(self, stored_view):
45 raise NotImplementedError("Subclass must implement abstract method")
47 def update_v3d(self, stored_view):
48 raise NotImplementedError("Subclass must implement abstract method")
50 @staticmethod
51 def is_modified(context, stored_view):
52 raise NotImplementedError("Subclass must implement abstract method")
55 class POV(StoredView):
56 def __init__(self, index=None):
57 super().__init__(mode='POV', index=index)
58 self.logger = logging.getLogger('%s.POV' % __name__)
60 def from_v3d(self, stored_view):
61 view3d = self.view3d
62 region3d = view3d.region_3d
64 stored_view.distance = region3d.view_distance
65 stored_view.location = region3d.view_location
66 stored_view.rotation = region3d.view_rotation
67 stored_view.perspective_matrix_md5 = POV._get_perspective_matrix_md5(region3d)
68 stored_view.perspective = region3d.view_perspective
69 stored_view.lens = view3d.lens
70 stored_view.clip_start = view3d.clip_start
71 stored_view.clip_end = view3d.clip_end
73 if region3d.view_perspective == 'CAMERA':
74 stored_view.camera_type = view3d.camera.type # type : 'CAMERA' or 'MESH'
75 stored_view.camera_name = view3d.camera.name # store string instead of object
76 if view3d.lock_object is not None:
77 stored_view.lock_object_name = view3d.lock_object.name # idem
78 else:
79 stored_view.lock_object_name = ""
80 stored_view.lock_cursor = view3d.lock_cursor
81 stored_view.cursor_location = bpy.context.scene.cursor.location
83 def update_v3d(self, stored_view):
84 view3d = self.view3d
85 region3d = view3d.region_3d
86 region3d.view_distance = stored_view.distance
87 region3d.view_location = stored_view.location
88 region3d.view_rotation = stored_view.rotation
89 region3d.view_perspective = stored_view.perspective
90 view3d.lens = stored_view.lens
91 view3d.clip_start = stored_view.clip_start
92 view3d.clip_end = stored_view.clip_end
93 view3d.lock_cursor = stored_view.lock_cursor
94 if stored_view.lock_cursor is True:
95 # update cursor only if view is locked to cursor
96 self.scene.cursor.location = stored_view.cursor_location
98 if stored_view.perspective == "CAMERA":
100 lock_obj = self._get_object(stored_view.lock_object_name)
101 if lock_obj:
102 view3d.lock_object = lock_obj
103 else:
104 cam = self._get_object(stored_view.camera_name)
105 if cam:
106 view3d.camera = cam
108 @staticmethod
109 def _get_object(name, pointer=None):
110 return bpy.data.objects.get(name)
112 @staticmethod
113 def is_modified(context, stored_view):
114 # TODO: check for others param, currently only perspective
115 # and perspective_matrix are checked
116 POV.logger = logging.getLogger('%s.POV' % __name__)
117 view3d = context.space_data
118 region3d = view3d.region_3d
119 if region3d.view_perspective != stored_view.perspective:
120 POV.logger.debug('view_perspective')
121 return True
123 md5 = POV._get_perspective_matrix_md5(region3d)
124 if (md5 != stored_view.perspective_matrix_md5 and
125 region3d.view_perspective != "CAMERA"):
126 POV.logger.debug('perspective_matrix')
127 return True
129 return False
131 @staticmethod
132 def _get_perspective_matrix_md5(region3d):
133 md5 = hashlib.md5(str(region3d.perspective_matrix).encode('utf-8')).hexdigest()
134 return md5
137 # class Layers(StoredView):
138 # def __init__(self, index=None):
139 # super().__init__(mode='COLLECTIONS', index=index)
140 # self.logger = logging.getLogger('%s.Layers' % __name__)
142 # def from_v3d(self, stored_view):
143 # view3d = self.view3d
144 # stored_view.view_layers = bpy.types.layer.collection
145 # stored_view.scene_layers = self.scene.layers
146 # stored_view.lock_camera = view3d.lock_camera
148 # def update_v3d(self, stored_view):
149 # view3d = self.view3d
150 # view3d.camera = stored_view.camera
151 # if stored_view.camera is True:
152 # self.scene.layers = stored_view.scene_layers
153 # else:
154 # view3d.layers = stored_view.view_layers
156 # @staticmethod
157 # def is_modified(context, stored_view):
158 # Layers.logger = logging.getLogger('%s.Layers' % __name__)
159 # if stored_view.camera != context.space_data.camera:
160 # Layers.logger.debug('lock_camera')
161 # return True
162 # if stored_view.camera is True:
163 # for i in range(20):
164 # if stored_view.scene_layers[i] != context.scene.layers[i]:
165 # Layers.logger.debug('scene_layers[%s]' % (i, ))
166 # return True
167 # else:
168 # for i in range(20):
169 # if stored_view.view_layers[i] != context.space_data.view3d.layers[i]:
170 # return True
171 # return False
174 # class Display(StoredView):
175 # def __init__(self, index=None):
176 # super().__init__(mode='DISPLAY', index=index)
177 # self.logger = logging.getLogger('%s.Display' % __name__)
179 # def from_v3d(self, stored_view):
180 # view3d = self.view3d
181 # stored_view.viewport_shade = view3d.viewport_shade
182 # stored_view.show_only_render = view3d.show_only_render
183 # stored_view.show_outline_selected = view3d.show_outline_selected
184 # stored_view.show_all_objects_origin = view3d.show_all_objects_origin
185 # stored_view.show_relationship_lines = view3d.show_relationship_lines
186 # stored_view.show_floor = view3d.show_floor
187 # stored_view.show_axis_x = view3d.show_axis_x
188 # stored_view.show_axis_y = view3d.show_axis_y
189 # stored_view.show_axis_z = view3d.show_axis_z
190 # stored_view.grid_lines = view3d.grid_lines
191 # stored_view.grid_scale = view3d.grid_scale
192 # stored_view.grid_subdivisions = view3d.grid_subdivisions
193 # stored_view.material_mode = self.scene.game_settings.material_mode
194 # stored_view.show_textured_solid = view3d.show_textured_solid
196 # def update_v3d(self, stored_view):
197 # view3d = self.view3d
198 # view3d.viewport_shade = stored_view.viewport_shade
199 # view3d.show_only_render = stored_view.show_only_render
200 # view3d.show_outline_selected = stored_view.show_outline_selected
201 # view3d.show_all_objects_origin = stored_view.show_all_objects_origin
202 # view3d.show_relationship_lines = stored_view.show_relationship_lines
203 # view3d.show_floor = stored_view.show_floor
204 # view3d.show_axis_x = stored_view.show_axis_x
205 # view3d.show_axis_y = stored_view.show_axis_y
206 # view3d.show_axis_z = stored_view.show_axis_z
207 # view3d.grid_lines = stored_view.grid_lines
208 # view3d.grid_scale = stored_view.grid_scale
209 # view3d.grid_subdivisions = stored_view.grid_subdivisions
210 # self.scene.game_settings.material_mode = stored_view.material_mode
211 # view3d.show_textured_solid = stored_view.show_textured_solid
213 # @staticmethod
214 # def is_modified(context, stored_view):
215 # Display.logger = logging.getLogger('%s.Display' % __name__)
216 # view3d = context.space_data
217 # excludes = ["material_mode", "quad_view", "lock_rotation", "show_sync_view", "use_box_clip", "name"]
218 # for k, v in stored_view.items():
219 # if k not in excludes:
220 # if getattr(view3d, k) != getattr(stored_view, k):
221 # return True
223 # if stored_view.material_mode != context.scene.game_settings.material_mode:
224 # Display.logger.debug('material_mode')
225 # return True
228 class View(StoredView):
229 def __init__(self, index=None):
230 super().__init__(mode='VIEW', index=index)
231 self.logger = logging.getLogger('%s.View' % __name__)
232 self.pov = POV()
233 # self.layers = Layers()
234 # self.display = Display()
236 def from_v3d(self, stored_view):
237 self.pov.from_v3d(stored_view.pov)
238 # self.layers.from_v3d(stored_view.layers)
239 # self.display.from_v3d(stored_view.display)
241 def update_v3d(self, stored_view):
242 self.pov.update_v3d(stored_view.pov)
243 # self.layers.update_v3d(stored_view.layers)
244 # self.display.update_v3d(stored_view.display)
246 @staticmethod
247 def is_modified(context, stored_view):
248 POV.is_modified(context, stored_view.pov) #or \
249 # Layers.is_modified(context, stored_view.layers) or \
250 # Display.is_modified(context, stored_view.display):
251 return True
252 # return False
255 class DataStore():
256 def __init__(self, scene=None, mode=None):
257 if scene is None:
258 scene = bpy.context.scene
259 stored_views = scene.stored_views
260 self.mode = mode
262 if mode is None:
263 self.mode = stored_views.mode
265 if self.mode == 'VIEW':
266 self.list = stored_views.view_list
267 self.current_index = stored_views.current_indices[0]
268 elif self.mode == 'POV':
269 self.list = stored_views.pov_list
270 self.current_index = stored_views.current_indices[1]
271 elif self.mode == 'LAYERS':
272 self.list = stored_views.layers_list
273 self.current_index = stored_views.current_indices[2]
274 elif self.mode == 'DISPLAY':
275 self.list = stored_views.display_list
276 self.current_index = stored_views.current_indices[3]
278 def create(self):
279 item = self.list.add()
280 item.name = self._generate_name()
281 index = len(self.list) - 1
282 self._set_current_index(index)
283 return item, index
285 def get(self, index):
286 self._set_current_index(index)
287 return self.list[index]
289 def delete(self, index):
290 if self.current_index > index:
291 self._set_current_index(self.current_index - 1)
292 elif self.current_index == index:
293 self._set_current_index(-1)
295 self.list.remove(index)
297 def _set_current_index(self, index):
298 self.current_index = index
299 mode = self.mode
300 stored_views = bpy.context.scene.stored_views
301 if mode == 'VIEW':
302 stored_views.current_indices[0] = index
303 elif mode == 'POV':
304 stored_views.current_indices[1] = index
305 elif mode == 'LAYERS':
306 stored_views.current_indices[2] = index
307 elif mode == 'DISPLAY':
308 stored_views.current_indices[3] = index
310 def _generate_name(self):
311 default_name = str(self.mode)
312 names = []
313 for i in self.list:
314 i_name = i.name
315 if i_name.startswith(default_name):
316 names.append(i_name)
317 names.sort()
318 try:
319 l_name = names[-1]
320 post_fix = l_name.rpartition('.')[2]
321 if post_fix.isnumeric():
322 post_fix = str(int(post_fix) + 1).zfill(3)
323 else:
324 if post_fix == default_name:
325 post_fix = "001"
326 return default_name + "." + post_fix
327 except:
328 return default_name
330 @staticmethod
331 def sanitize_data(scene):
333 def check_objects_references(mode, list):
334 to_remove = []
335 for i, list_item in enumerate(list.items()):
336 key, item = list_item
337 if mode == 'POV' or mode == 'VIEWS':
338 if mode == 'VIEWS':
339 item = item.pov
341 if item.perspective == "CAMERA":
343 camera = bpy.data.objects.get(item.camera_name)
344 if camera is None:
345 try: # pick a default camera TODO: ask to pick?
346 camera = bpy.data.cameras[0]
347 item.camera_name = camera.name
348 except: # couldn't find a camera in the scene
349 pass
351 obj = bpy.data.objects.get(item.lock_object_name)
352 if obj is None and camera is None:
353 to_remove.append(i)
355 for i in reversed(to_remove):
356 list.remove(i)
358 modes = ['POV', 'VIEW', 'DISPLAY', 'LAYERS']
359 for mode in modes:
360 data = DataStore(scene=scene, mode=mode)
361 check_objects_references(mode, data.list)
364 def stored_view_factory(mode, *args, **kwargs):
365 if mode == 'POV':
366 return POV(*args, **kwargs)
367 elif mode == 'LAYERS':
368 return Layers(*args, **kwargs)
369 elif mode == 'DISPLAY':
370 return Display(*args, **kwargs)
371 elif mode == 'VIEW':
372 return View(*args, **kwargs)