diff --git a/model/arc.py b/model/arc.py index 8ba0f23b..19be3060 100644 --- a/model/arc.py +++ b/model/arc.py @@ -11,9 +11,9 @@ from bpy.utils import register_classes_factory from ..solver import Solver -from .base_entity import SlvsGenericEntity +from .base_entity import SlvsGenericEntity, tag_update from .base_entity import Entity2D -from .utilities import slvs_entity_pointer, tag_update +from .utilities import slvs_entity_pointer from .constants import CURVE_RESOLUTION from ..utilities.constants import HALF_TURN, FULL_TURN, QUARTER_TURN from ..utilities.math import range_2pi, pol2cart diff --git a/model/base_entity.py b/model/base_entity.py index a198f34b..a93b4b73 100644 --- a/model/base_entity.py +++ b/model/base_entity.py @@ -1,7 +1,13 @@ +""" +Base entity classes for CAD Sketcher. + +""" + import logging from typing import List import gpu +from bpy import app from bpy.props import IntProperty, StringProperty, BoolProperty from bpy.types import Context @@ -17,6 +23,12 @@ logger = logging.getLogger(__name__) +def tag_update(self, _context=None): + # context argument ignored + if not self.is_dirty: + self.is_dirty = True + + class SlvsGenericEntity: def entity_name_getter(self): return self.get("name", str(self)) @@ -34,7 +46,7 @@ def entity_name_setter(self, new_name): fixed: BoolProperty(name="Fixed", update=update_system_cb) visible: BoolProperty(name="Visible", default=True, update=update_cb) origin: BoolProperty(name="Origin") - construction: BoolProperty(name="Construction") + construction: BoolProperty(name="Construction", update=tag_update) props = () dirty: BoolProperty(name="Needs Update", default=True, options={"SKIP_SAVE"}) @@ -64,8 +76,10 @@ def is_dirty(self, value: bool): @property def _shader(self): if self.is_point(): - return Shaders.uniform_color_3d() - return Shaders.uniform_color_line_3d() + return Shaders.point_color_3d() + if self.is_dashed(): + return Shaders.uniform_color_line_3d() + return Shaders.polyline_color_3d() @property def _id_shader(self): @@ -90,7 +104,7 @@ def line_width(self): @property def line_width_select(self): - return 4 * preferences.get_scale() + return 4 * self.line_width def __str__(self): _, local_index = breakdown_index(self.slvs_index) @@ -213,7 +227,7 @@ def is_dashed(self): def draw(self, context): if not self.is_visible(context): - return None + return batch = self._batch if not batch: @@ -221,19 +235,24 @@ def draw(self, context): shader = self._shader shader.bind() - gpu.state.blend_set("ALPHA") - gpu.state.point_size_set(self.point_size) col = self.color(context) shader.uniform_float("color", col) - if not self.is_point(): - shader.uniform_bool("dashed", (self.is_dashed(),)) - shader.uniform_float("dash_width", 0.05) - shader.uniform_float("dash_factor", 0.3) + if self.is_point(): + gpu.state.point_size_set(self.point_size) + else: gpu.state.line_width_set(self.line_width) + if self.is_dashed(): + shader.uniform_bool("dashed", (self.is_dashed(),)) + shader.uniform_float("dash_width", 0.05) + shader.uniform_float("dash_factor", 0.3) + elif app.version >= (4, 5): + shader.uniform_float("lineWidth", self.line_width) + shader.uniform_float("viewportSize", (context.region.width, context.region.height)) + batch.draw(shader) gpu.shader.unbind() self.restore_opengl_defaults() @@ -254,10 +273,10 @@ def draw_id(self, context): shader.uniform_float("color", (*index_to_rgb(self.slvs_index), 1.0)) if not self.is_point(): - # viewport = [context.area.width, context.area.height] - # shader.uniform_float("Viewport", viewport) - shader.uniform_bool("dashed", (False,)) gpu.state.line_width_set(self.line_width_select) + if app.version >= (4, 5): + shader.uniform_float("lineWidth", self.line_width_select) + shader.uniform_float("viewportSize", (context.region.width, context.region.height)) batch.draw(shader) gpu.shader.unbind() @@ -331,11 +350,6 @@ def draw_props(self, layout): return sub - def tag_update(self, _context=None): - # context argument ignored - if not self.is_dirty: - self.is_dirty = True - def new(self, context: Context, **kwargs): """Create new entity based on this instance""" raise NotImplementedError diff --git a/model/circle.py b/model/circle.py index fccacee2..21e268f4 100644 --- a/model/circle.py +++ b/model/circle.py @@ -12,9 +12,9 @@ from ..solver import Solver from ..utilities.math import range_2pi, pol2cart -from .base_entity import SlvsGenericEntity +from .base_entity import SlvsGenericEntity, tag_update from .base_entity import Entity2D -from .utilities import slvs_entity_pointer, tag_update +from .utilities import slvs_entity_pointer from .constants import CURVE_RESOLUTION from ..utilities.constants import HALF_TURN, FULL_TURN from ..utilities.draw import coords_arc_2d diff --git a/model/normal_3d.py b/model/normal_3d.py index 74785dd1..e584a706 100644 --- a/model/normal_3d.py +++ b/model/normal_3d.py @@ -6,7 +6,7 @@ from mathutils import Euler from ..solver import Solver -from .base_entity import SlvsGenericEntity +from .base_entity import SlvsGenericEntity, tag_update logger = logging.getLogger(__name__) @@ -50,7 +50,7 @@ def set_orientation(self, value): description="Quaternion which describes the orientation of the normal", subtype="QUATERNION", size=4, - update=SlvsGenericEntity.tag_update, + update=tag_update, ) ui_orientation: FloatVectorProperty( @@ -60,7 +60,7 @@ def set_orientation(self, value): get=get_orientation, set=set_orientation, options={"SKIP_SAVE"}, - update=SlvsGenericEntity.tag_update, + update=tag_update, ) props = ("ui_orientation",) diff --git a/model/point_2d.py b/model/point_2d.py index 4d01a124..b13a1fe9 100644 --- a/model/point_2d.py +++ b/model/point_2d.py @@ -10,10 +10,10 @@ from ..utilities.draw import draw_rect_2d from ..solver import Solver -from .base_entity import SlvsGenericEntity -from .base_entity import Entity2D +from .base_entity import SlvsGenericEntity, Entity2D, tag_update from .utilities import slvs_entity_pointer, make_coincident from .line_2d import SlvsLine2D +from ..utilities.constants import HALF_TURN logger = logging.getLogger(__name__) @@ -79,8 +79,9 @@ class SlvsPoint2D(Point2D, PropertyGroup): subtype="XYZ", size=2, unit="LENGTH", - update=SlvsGenericEntity.tag_update, + update=tag_update, ) + props = ("co",) def dependencies(self) -> List[SlvsGenericEntity]: return [ diff --git a/model/point_3d.py b/model/point_3d.py index fac7162b..0f82cf3b 100644 --- a/model/point_3d.py +++ b/model/point_3d.py @@ -9,6 +9,7 @@ from ..utilities.draw import draw_cube_3d from ..solver import Solver from .base_entity import SlvsGenericEntity +from .base_entity import tag_update logger = logging.getLogger(__name__) @@ -60,7 +61,7 @@ class SlvsPoint3D(Point3D, PropertyGroup): description="The location of the point", subtype="XYZ", unit="LENGTH", - update=SlvsGenericEntity.tag_update, + update=tag_update, ) props = ("location",) diff --git a/operators/select_box.py b/operators/select_box.py index e3cfe31e..d96eb2eb 100644 --- a/operators/select_box.py +++ b/operators/select_box.py @@ -9,6 +9,7 @@ from ..utilities.index import rgb_to_index from ..utilities.view import refresh from ..utilities.select import mode_property, deselect_all +from ..shaders import Shaders def get_start_dist(value1, value2, invert: bool = False): @@ -19,9 +20,21 @@ def get_start_dist(value1, value2, invert: bool = False): def draw_callback_px(self, context): - shader = gpu.shader.from_builtin("UNIFORM_COLOR") + """Draw selection box with appropriate shader based on GPU backend.""" + # Simple backend detection + try: + backend_type = gpu.platform.backend_type_get() + is_vulkan_metal = backend_type in ('VULKAN', 'METAL') + except: + is_vulkan_metal = False + + # Use appropriate shader + if is_vulkan_metal: + shader = gpu.shader.from_builtin("UNIFORM_COLOR") + else: + shader = Shaders.uniform_color_line_2d() + gpu.state.blend_set("ALPHA") - gpu.state.line_width_set(2.0) start = self.start_coords end = self.mouse_pos @@ -30,9 +43,18 @@ def draw_callback_px(self, context): batch = batch_for_shader(shader, "LINE_STRIP", {"pos": box_path}) shader.bind() shader.uniform_float("color", (0.0, 0.0, 0.0, 0.5)) + + # Set line width uniforms for custom shader only + if not is_vulkan_metal: + try: + shader.uniform_float("lineWidth", 2.0) + except: + pass + + gpu.state.line_width_set(2.0) batch.draw(shader) - # restore opengl defaults + # Restore OpenGL defaults gpu.state.line_width_set(1.0) gpu.state.blend_set("NONE") diff --git a/operators/update.py b/operators/update.py index c873a940..3d03e4fa 100644 --- a/operators/update.py +++ b/operators/update.py @@ -1,23 +1,22 @@ -from bpy.types import Operator, Context +import bpy +from bpy.types import Operator from bpy.utils import register_classes_factory -from ..declarations import Operators from ..solver import Solver from ..converters import update_convertor_geometry -class View3D_OT_update(Operator): - """Solve all sketches and update converted geometry""" +class VIEW3D_OT_update(Operator): + bl_idname = "view3d.slvs_update" + bl_label = "Update" - bl_idname = Operators.Update - bl_label = "Force Update" + def execute(self, context): + update_convertor_geometry() + solvesys = Solver() + solvesys.solve() + return {'FINISHED'} - def execute(self, context: Context): - solver = Solver(context, None, all=True) - solver.solve() - update_convertor_geometry(context.scene) - return {"FINISHED"} - - -register, unregister = register_classes_factory((View3D_OT_update,)) +register, unregister = register_classes_factory(( + VIEW3D_OT_update, +)) diff --git a/shaders.py b/shaders.py index 46d776fb..7b021018 100644 --- a/shaders.py +++ b/shaders.py @@ -57,6 +57,17 @@ class Shaders: } """ + base_vertex_shader_2d = """ + void main() { + gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0, 1.0f); + } + """ + base_fragment_shader_2d = """ + void main() { + fragColor = color; + } + """ + @classmethod def get_base_shader_3d_info(cls): @@ -82,6 +93,21 @@ def get_base_shader_3d_info(cls): return shader_info + @classmethod + def get_base_shader_2d_info(cls): + + shader_info = GPUShaderCreateInfo() + shader_info.push_constant("MAT4", "ModelViewProjectionMatrix") + shader_info.push_constant("VEC4", "color") + shader_info.push_constant("FLOAT", "lineWidth") + shader_info.vertex_in(0, "VEC2", "pos") + shader_info.fragment_out(0, "VEC4", "fragColor") + + shader_info.vertex_source(cls.base_vertex_shader_2d) + shader_info.fragment_source(cls.base_fragment_shader_2d) + + return shader_info + @staticmethod @cache def uniform_color_3d(): @@ -89,6 +115,23 @@ def uniform_color_3d(): return gpu.shader.from_builtin("3D_UNIFORM_COLOR") return gpu.shader.from_builtin("UNIFORM_COLOR") + @classmethod + @cache + def point_color_3d(cls): + """Get uniform color shader for points. Compatible with all GPU backends.""" + if app.version < (4, 5): + return cls.uniform_color_3d() + return gpu.shader.from_builtin("POINT_UNIFORM_COLOR") + + @classmethod + @cache + def polyline_color_3d(cls): + """Get polyline shader for thick lines""" + if app.version < (4, 5): + return cls.uniform_color_3d() + return gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR") + + @classmethod @cache def uniform_color_image_2d(cls): @@ -135,7 +178,7 @@ def uniform_color_image_2d(cls): @classmethod @cache def id_line_3d(cls): - shader = cls.uniform_color_line_3d() + shader = cls.polyline_color_3d() return shader @classmethod @@ -147,66 +190,16 @@ def uniform_color_line_3d(cls): del shader_info return shader - @staticmethod + @classmethod @cache - def id_shader_3d(): - shader_info = GPUShaderCreateInfo() - shader_info.push_constant("MAT4", "ModelViewProjectionMatrix") - shader_info.push_constant("VEC4", "color") - shader_info.vertex_in(0, "VEC3", "pos") - shader_info.fragment_out(0, "VEC4", "fragColor") - - shader_info.vertex_source( - """ - void main() - { - gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0); - } - """ - ) + def id_shader_3d(cls): + shader = cls.point_color_3d() + return shader - shader_info.fragment_source( - """ - void main() - { - fragColor = color; - } - """ - ) - + @classmethod + @cache + def uniform_color_line_2d(cls): + shader_info = cls.get_base_shader_2d_info() shader = create_from_info(shader_info) del shader_info return shader - - @staticmethod - @cache - def dashed_uniform_color_3d(): - vertex_shader = """ - uniform mat4 ModelViewProjectionMatrix; - in vec3 pos; - in float arcLength; - - out float v_ArcLength; - vec4 project = ModelViewProjectionMatrix * vec4(pos, 1.0f); - vec4 offset = vec4(0,0,-0.001,0); - void main() - { - v_ArcLength = arcLength; - gl_Position = project + offset; - } - """ - - fragment_shader = """ - uniform float u_Scale; - uniform vec4 color; - - in float v_ArcLength; - out vec4 fragColor; - - void main() - { - if (step(sin(v_ArcLength * u_Scale), 0.7) == 0) discard; - fragColor = color; - } - """ - return GPUShader(vertex_shader, fragment_shader) diff --git a/ui/panels/tools.py b/ui/panels/tools.py index a9f9e063..1da81968 100644 --- a/ui/panels/tools.py +++ b/ui/panels/tools.py @@ -32,8 +32,9 @@ def draw(self, context: Context): layout.prop(context.scene.sketcher, "use_construction") # Node modifier operators - layout.label(text="Node Tools:") - col = layout.column(align=True) - # col.operator(declarations.Operators.NodeFill) - col.operator(declarations.Operators.NodeExtrude) - col.operator(declarations.Operators.NodeArrayLinear) + if is_experimental(): + layout.label(text="Node Tools:") + col = layout.column(align=True) + #col.operator(declarations.Operators.NodeFill) + col.operator(declarations.Operators.NodeExtrude) + col.operator(declarations.Operators.NodeArrayLinear)