# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### import logging import time import bpy from bpy.props import StringProperty from bpy.types import Gizmo, GizmoGroup, Operator from mathutils import Matrix from . import ( client_lib, datas, global_vars, icons, ratings_utils, search, ui, ui_panels, utils, ) bk_logger = logging.getLogger(__name__) def get_assets_for_rating(): """Get assets from scene that could/should be rated by the user. TODO: this is only a draft""" assets = [] for ob in bpy.context.scene.objects: if should_be_rated(ob): assets.append(ob) for m in bpy.data.materials: if m.get("asset_data"): assets.append(m) for b in bpy.data.brushes: if b.get("asset_data"): assets.append(b) return assets asset_types = ( ("MODEL", "Model", "set of objects"), ("PRINTABLE", "Printable", "3D printable model"), ("SCENE", "Scene", "scene"), ("HDR", "HDR", "hdr"), ("MATERIAL", "Material", "any .blend Material"), ("TEXTURE", "Texture", "a texture, or texture set"), ("BRUSH", "Brush", "brush, can be any type of blender brush"), ("ADDON", "Addon", "addnon"), ) def draw_ratings_menu(self, context, layout): pcoll = icons.icon_collections["main"] if not utils.user_logged_in(): user_preferences = bpy.context.preferences.addons[__package__].preferences if user_preferences.login_attempt: ui_panels.draw_login_progress(layout) else: layout.operator_context = "EXEC_DEFAULT" layout.operator( "wm.blenderkit_login", text="Login to Rate and Comment assets", icon="URL", ).signup = False return col = layout.column() # layout.template_icon_view(bkit_ratings, property, show_labels=False, scale=6.0, scale_popup=5.0) row = col.row() if self.asset_data.get("canDownload") is not True: row.label(text="Asset in Full Plan. Subscribe to rate it.", icon="SOLO_ON") return profile_name = "" profile = global_vars.BKIT_PROFILE if profile and len(profile.firstName) > 0: profile_name = " " + profile.firstName row.label(text="Rate Quality:", icon="SOLO_ON") # row = col.row() # row.label(text='Please help the community by rating quality:') row = col.row() row.prop(self, "rating_quality_ui", expand=True, icon_only=True, emboss=False) if self.rating_quality > 0: row.label(text=f" Thanks{profile_name}!", icon="FUND") col.separator() col.separator() row = col.row() row.label(text="Rate Complexity:", icon_value=pcoll["dumbbell"].icon_id) row = col.row() row.label(text=f"How many hours did this {self.asset_type} save you?") if utils.profile_is_validator(): row = col.row() row.prop(self, "rating_work_hours") row = col.row() row.prop(self, "rating_work_hours_ui", expand=True, icon_only=False, emboss=True) if self.rating_work_hours > 100: utils.label_multiline( col, text=f"\nThat's huge! please be sure to give such rating only to godly {self.asset_type}s.\n", width=300, ) elif float(self.rating_work_hours) > 18: col.separator() utils.label_multiline( col, text=f"\nThat's a lot! please be sure to give such rating only to amazing {self.asset_type}s.\n", width=300, ) if self.rating_work_hours > 0: row = col.row() row.label(text=f"Thanks{profile_name}, you are amazing!", icon="FUND") class FastRateMenu(Operator, ratings_utils.RatingProperties): """Rating of the assets, also directly from the asset bar - without need to download assets""" bl_idname = "wm.blenderkit_menu_rating_upload" bl_label = "Ratings" bl_options = {"REGISTER", "UNDO", "INTERNAL"} @classmethod def poll(cls, context): return True def draw(self, context): # when rating gets recieved while the window is already open, we need to prefill. self.prefill_ratings() layout = self.layout layout.label(text=f"Rating of the {self.asset_type}: {self.asset_data['name']}") draw_ratings_menu(self, context, layout) layout.template_icon(icon_value=self.img.preview.icon_id, scale=12) def execute(self, context): ui_props = bpy.context.window_manager.blenderkitUI # get asset id if ui_props.active_index > -1: sr = search.get_search_results() self.asset_data = dict(sr[ui_props.active_index]) self.asset_id = self.asset_data["id"] self.asset_type = self.asset_data["assetType"] else: if bpy.context.view_layer.objects.active is not None: ob = utils.get_active_model() ad = ob.get("asset_data") if ad: self.asset_data = ad self.asset_id = self.asset_data["id"] self.asset_type = self.asset_data["assetType"] self.asset = ob if self.asset_id == "": return {"CANCELLED"} wm = context.window_manager self.img = ui.get_large_thumbnail_image(self.asset_data) utils.img_to_preview(self.img, copy_original=True) ratings_utils.ensure_rating(self.asset_id) self.prefill_ratings() # Update last popup activity time to prevent shortcut interference from . import ui_panels ui_panels.last_time_dropdown_active = time.time() if self.asset_type in ("model", "scene"): # spawn a wider one for validators for the enum buttons return wm.invoke_popup(self, width=400) else: return wm.invoke_popup(self, width=250) class SetBookmark(bpy.types.Operator): """Add or remove bookmarking of the asset.\nShortcut: hover over asset in the asset bar and press 'B'.""" bl_idname = "wm.blenderkit_bookmark_asset" bl_label = "BlenderKit bookmark assets" bl_options = {"REGISTER", "UNDO", "INTERNAL"} asset_id: StringProperty( # type: ignore[valid-type] name="Asset Base Id", description="Unique id of the asset (hidden)", default="", options={"SKIP_SAVE"}, ) # bookmark: bpy.props.BoolProperty( # name="bookmark", # description="Pass current state of bookmark, gets inverted", # default=True) @classmethod def poll(cls, context): return True def execute(self, context): rating = ratings_utils.get_rating_local(self.asset_id) if rating is None: rating = datas.AssetRating() if rating.bookmarks == 1: bookmark_value = 0 else: bookmark_value = 1 ratings_utils.store_rating_local( self.asset_id, rating_type="bookmarks", value=bookmark_value ) client_lib.send_rating(self.asset_id, "bookmarks", str(bookmark_value)) return {"FINISHED"} ## NOT USED ANYMORE # def rating_menu_draw(self, context): # layout = self.layout # ui_props = context.window_manager.blenderkitUI # sr = search.get_search_results() # asset_search_index = ui_props.active_index # if asset_search_index > -1: # asset_data = dict(sr["results"][asset_search_index]) # col = layout.column() # layout.label(text="Admin rating Tools:") # col.operator_context = "INVOKE_DEFAULT" # op = col.operator("wm.blenderkit_menu_rating_upload", text="Add Rating") # op.asset_id = asset_data["id"] # op.asset_name = asset_data["name"] # op.asset_type = asset_data["assetType"] # Coordinates (each one is a triangle). custom_shape_verts = ( (0.1896940916776657, 0.2608509361743927, 0.0), (0.2438376545906067, 0.09421423077583313, 0.0), (0.2979812026023865, 0.2608509361743927, 0.0), (0.1896940916776657, 0.2608509361743927, 0.0), (0.052547797560691833, 0.2484826147556305, 0.0), (0.15623150765895844, 0.1578637957572937, 0.0), (0.15623150765895844, 0.1578637957572937, 0.0), (0.12561391294002533, 0.023607879877090454, 0.0), (0.2438376545906067, 0.09421423077583313, 0.0), (0.2438376545906067, 0.09421423077583313, 0.0), (0.36206138134002686, 0.023607879877090454, 0.0), (0.33144378662109375, 0.1578637957572937, 0.0), (0.33144378662109375, 0.1578637957572937, 0.0), (0.4351276159286499, 0.2484826147556305, 0.0), (0.2979812026023865, 0.2608509361743927, 0.0), (0.2979812026023865, 0.2608509361743927, 0.0), (0.2438376396894455, 0.3874630033969879, 0.0), (0.1896940916776657, 0.2608509361743927, 0.0), (0.1896940916776657, 0.2608509361743927, 0.0), (0.15623150765895844, 0.1578637957572937, 0.0), (0.2438376545906067, 0.09421423077583313, 0.0), (0.2438376545906067, 0.09421423077583313, 0.0), (0.33144378662109375, 0.1578637957572937, 0.0), (0.2979812026023865, 0.2608509361743927, 0.0), ) class RatingStarWidget(Gizmo): bl_idname = "VIEW3D_GT_custom_shape_widget" __slots__ = ( "custom_shape", "init_mouse_y", "init_value", ) def _update_draw_matrix(self): R = bpy.context.region_data.view_rotation.to_matrix().to_4x4() loc, _, scale = self.matrix_basis.decompose() self.matrix_basis = Matrix.Translation(loc) @ R @ Matrix.Diagonal(scale.to_4d()) def draw(self, context): self._update_draw_matrix() self.draw_custom_shape(self.custom_shape) def draw_select(self, context, select_id): self._update_draw_matrix() self.draw_custom_shape(self.custom_shape, select_id=select_id) def setup(self): if not hasattr(self, "custom_shape"): self.custom_shape = self.new_custom_shape("TRIS", custom_shape_verts) def invoke(self, context, event): return {"RUNNING_MODAL"} def exit(self, context, cancel): pass def modal(self, context, event, tweak): return {"FINISHED"} def should_be_rated(ob) -> bool: ad = ob.get("asset_data") if ad is None: return False rating = ratings_utils.get_rating_local(ad["id"]) if rating is None: # is None would work too, but would show rating option and then hide it when the assets are already rated return True return False class RatingStarWidgetGroup(GizmoGroup): bl_idname = "OBJECT_GGT_light_test" bl_label = "Test Light Widget" bl_space_type = "VIEW_3D" bl_region_type = "WINDOW" bl_options = {"3D", "PERSISTENT"} @classmethod def poll(cls, context): if not utils.profile_is_validator(): return False if bpy.context.view_layer.objects.active is not None: ob = utils.get_active_model() return should_be_rated(ob) return False def setup(self, context): ob = utils.get_active_model() gz = self.gizmos.new(RatingStarWidget.bl_idname) props = gz.target_set_operator("wm.blenderkit_menu_rating_upload") props.asset_id = ob["asset_data"]["assetBaseId"] gz.color = 0.5, 0.5, 0.0 gz.alpha = 0.5 gz.color_highlight = 1.0, 1.0, 1.0 gz.alpha_highlight = 0.5 gz.scale_basis = 1 gz.use_draw_modal = True self.energy_gizmo = gz def refresh(self, context): ob = utils.get_active_model() gz = self.energy_gizmo R = bpy.context.region_data.view_rotation.to_matrix().to_4x4() loc, _, _ = ob.matrix_world.decompose() _, _, scale = gz.matrix_basis.decompose() gz.matrix_basis = Matrix.Translation(loc) @ R @ Matrix.Diagonal(scale.to_4d()) classes = ( FastRateMenu, SetBookmark, RatingStarWidget, RatingStarWidgetGroup, ratings_utils.RatingProperties, # ratings_utils.RatingPropsCollection, ) def register_ratings(): for cls in classes: bpy.utils.register_class(cls) def unregister_ratings(): for cls in classes: bpy.utils.unregister_class(cls)