1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Authors: nfloyd, Francesco Siddi
8 module_logger
= logging
.getLogger(__name__
)
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)
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
29 self
.data_store
= DataStore(mode
=mode
)
33 stored_view
, self
.index
= self
.data_store
.create()
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
))
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")
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
):
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
_md
5(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
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
):
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
)
102 view3d
.lock_object
= lock_obj
104 cam
= self
._get
_object
(stored_view
.camera_name
)
109 def _get_object(name
, pointer
=None):
110 return bpy
.data
.objects
.get(name
)
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')
123 md5
= POV
._get
_perspective
_matrix
_md
5(region3d
)
124 if (md5
!= stored_view
.perspective_matrix_md5
and
125 region3d
.view_perspective
!= "CAMERA"):
126 POV
.logger
.debug('perspective_matrix')
132 def _get_perspective_matrix_md5(region3d
):
133 md5
= hashlib
.md5(str(region3d
.perspective_matrix
).encode('utf-8')).hexdigest()
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
154 # view3d.layers = stored_view.view_layers
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')
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, ))
168 # for i in range(20):
169 # if stored_view.view_layers[i] != context.space_data.view3d.layers[i]:
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
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):
223 # if stored_view.material_mode != context.scene.game_settings.material_mode:
224 # Display.logger.debug('material_mode')
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__
)
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)
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):
256 def __init__(self
, scene
=None, mode
=None):
258 scene
= bpy
.context
.scene
259 stored_views
= scene
.stored_views
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]
279 item
= self
.list.add()
280 item
.name
= self
._generate
_name
()
281 index
= len(self
.list) - 1
282 self
._set
_current
_index
(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
300 stored_views
= bpy
.context
.scene
.stored_views
302 stored_views
.current_indices
[0] = index
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
)
315 if i_name
.startswith(default_name
):
320 post_fix
= l_name
.rpartition('.')[2]
321 if post_fix
.isnumeric():
322 post_fix
= str(int(post_fix
) + 1).zfill(3)
324 if post_fix
== default_name
:
326 return default_name
+ "." + post_fix
331 def sanitize_data(scene
):
333 def check_objects_references(mode
, list):
335 for i
, list_item
in enumerate(list.items()):
336 key
, item
= list_item
337 if mode
== 'POV' or mode
== 'VIEWS':
341 if item
.perspective
== "CAMERA":
343 camera
= bpy
.data
.objects
.get(item
.camera_name
)
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
351 obj
= bpy
.data
.objects
.get(item
.lock_object_name
)
352 if obj
is None and camera
is None:
355 for i
in reversed(to_remove
):
358 modes
= ['POV', 'VIEW', 'DISPLAY', 'LAYERS']
360 data
= DataStore(scene
=scene
, mode
=mode
)
361 check_objects_references(mode
, data
.list)
364 def stored_view_factory(mode
, *args
, **kwargs
):
366 return POV(*args
, **kwargs
)
367 elif mode
== 'LAYERS':
368 return Layers(*args
, **kwargs
)
369 elif mode
== 'DISPLAY':
370 return Display(*args
, **kwargs
)
372 return View(*args
, **kwargs
)