2025-07-01
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
'''
|
||||
Copyright (C) 2023 CG Cookie
|
||||
http://cgcookie.com
|
||||
hello@cgcookie.com
|
||||
|
||||
Created by Jonathan Denning, Jonathan Williamson
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import bpy
|
||||
|
||||
from mathutils import Matrix, Vector
|
||||
from bpy_extras.view3d_utils import (
|
||||
location_3d_to_region_2d,
|
||||
region_2d_to_vector_3d,
|
||||
region_2d_to_location_3d,
|
||||
region_2d_to_origin_3d,
|
||||
)
|
||||
|
||||
from ...config.options import options
|
||||
from ...addon_common.common.debug import dprint
|
||||
from ...addon_common.common.profiler import profiler
|
||||
from ...addon_common.common.maths import Point, Vec, Direction, Normal
|
||||
from ...addon_common.common.maths import Ray, XForm, Plane
|
||||
from ...addon_common.common.maths import Point2D, Vec2D, Direction2D
|
||||
from ...addon_common.common.decorators import blender_version_wrapper
|
||||
|
||||
|
||||
class RetopoFlow_Spaces:
|
||||
'''
|
||||
converts entities between screen space and world space
|
||||
|
||||
Note: if 2D is not specified, then it is a 1D or 3D entity (whichever is applicable)
|
||||
'''
|
||||
|
||||
def update_clip_settings(self, *, rescale=True):
|
||||
if options['clip auto adjust']:
|
||||
# adjust clipping settings
|
||||
view_origin = self.drawing.get_view_origin(orthographic_distance=1000)
|
||||
view_focus = self.actions.r3d.view_location
|
||||
bbox = self.sources_bbox
|
||||
closest = bbox.closest_Point(view_origin)
|
||||
farthest = bbox.farthest_Point(view_origin)
|
||||
self.drawing.space.clip_start = max(
|
||||
options['clip auto start min'],
|
||||
(view_origin - closest).length * options['clip auto start mult'],
|
||||
)
|
||||
self.drawing.space.clip_end = min(
|
||||
options['clip auto end max'],
|
||||
(view_origin - farthest).length * options['clip auto end mult'],
|
||||
)
|
||||
# print(f'clip auto adjusting')
|
||||
# print(f' origin: {view_origin}')
|
||||
# print(f' focus: {view_focus}')
|
||||
# print(f' closest: {closest}')
|
||||
# print(f' farthest: {farthest}')
|
||||
# print(f' dist from origin to closest: {(view_origin - closest).length}')
|
||||
# print(f' dist from origin to farthest: {(view_origin - farthest).length}')
|
||||
# print(f' dist from origin to focus: {(view_origin - view_focus).length}')
|
||||
# print(f' clip_start: {self.drawing.space.clip_start}')
|
||||
# print(f' clip_end: {self.drawing.space.clip_end}')
|
||||
elif rescale:
|
||||
self.end_normalize(self.context)
|
||||
self.start_normalize()
|
||||
# self.unscale_from_unit_box()
|
||||
# self.scale_to_unit_box(
|
||||
# clip_override=options['clip override'],
|
||||
# clip_start=options['clip start override'],
|
||||
# clip_end=options['clip end override'],
|
||||
# )
|
||||
|
||||
|
||||
|
||||
def get_view_origin(self):
|
||||
# does not work in ORTHO
|
||||
view_loc = self.actions.r3d.view_location
|
||||
view_dist = self.actions.r3d.view_distance
|
||||
view_rot = self.actions.r3d.view_rotation
|
||||
view_cam = Point(view_loc + (view_rot @ Vector((0,0,view_dist))))
|
||||
return view_cam
|
||||
|
||||
def get_view_direction(self):
|
||||
view_rot = self.actions.r3d.view_rotation
|
||||
return Direction(view_rot @ Vector((0, 0, -1)))
|
||||
|
||||
def Point2D_to_Vec(self, xy:Point2D):
|
||||
if xy is None: return None
|
||||
v = region_2d_to_vector_3d(self.actions.region, self.actions.r3d, xy)
|
||||
if v is None: return None
|
||||
return Vec(v)
|
||||
|
||||
def Point2D_to_Direction(self, xy:Point2D):
|
||||
if xy is None: return None
|
||||
d = region_2d_to_vector_3d(self.actions.region, self.actions.r3d, xy)
|
||||
if d is None: return None
|
||||
return Direction(d)
|
||||
|
||||
def Point2D_to_Origin(self, xy:Point2D):
|
||||
if xy is None: return None
|
||||
o = region_2d_to_origin_3d(self.actions.region, self.actions.r3d, xy)
|
||||
if o is None: return None
|
||||
return Point(o)
|
||||
|
||||
def Point2D_to_Ray(self, xy:Point2D, *, min_dist=0.0):
|
||||
if xy is None: return None
|
||||
o, d = self.Point2D_to_Origin(xy), self.Point2D_to_Direction(xy)
|
||||
if o is None or d is None: return None
|
||||
return Ray(o, d, min_dist=min_dist)
|
||||
|
||||
def Point2D_to_Point(self, xy:Point2D, depth:float):
|
||||
r = self.Point2D_to_Ray(xy)
|
||||
if r is None or r.o is None or r.d is None or depth is None:
|
||||
# dprint(r)
|
||||
pass
|
||||
# dprint(depth)
|
||||
pass
|
||||
return None
|
||||
return r.eval(depth) # Point(r.o + depth * r.d)
|
||||
#return Point(region_2d_to_location_3d(self.actions.region, self.actions.r3d, xy, depth))
|
||||
|
||||
def Point2D_to_Plane(self, xy0:Point2D, xy1:Point2D):
|
||||
ray0,ray1 = self.Point2D_to_Ray(xy0),self.Point2D_to_Ray(xy1)
|
||||
o = ray0.o + ray0.d
|
||||
n = Normal((ray1.o + ray1.d - o).cross(ray0.d))
|
||||
return Plane(o, n)
|
||||
|
||||
def Point_to_Point2D(self, xyz:Point):
|
||||
if not xyz: return None
|
||||
xy = location_3d_to_region_2d(self.actions.region, self.actions.r3d, xyz)
|
||||
if xy is None: return None
|
||||
return Point2D(xy)
|
||||
|
||||
alerted_small_clip_start = False
|
||||
def Point_to_depth(self, xyz):
|
||||
'''
|
||||
computes the distance of point (xyz) from view camera
|
||||
'''
|
||||
|
||||
if not xyz: return None
|
||||
xy = self.Point_to_Point2D(xyz)
|
||||
if xy is None: return None
|
||||
oxyz = self.Point2D_to_Origin(xy)
|
||||
return (xyz - oxyz).length
|
||||
|
||||
def Point_to_Direction(self, xyz:Point):
|
||||
if not xyz: return None
|
||||
xy = location_3d_to_region_2d(self.actions.region, self.actions.r3d, xyz)
|
||||
return self.Point2D_to_Direction(xy)
|
||||
|
||||
# @profiler.function
|
||||
def Point_to_Ray(self, xyz:Point, min_dist=0, max_dist_offset=0):
|
||||
if not xyz: return None
|
||||
xy = location_3d_to_region_2d(self.actions.region, self.actions.r3d, xyz)
|
||||
if not xy: return None
|
||||
o = self.Point2D_to_Origin(xy)
|
||||
#return Ray.from_segment(o, xyz)
|
||||
d = self.Point2D_to_Vec(xy)
|
||||
if o is None or d is None: return None
|
||||
dist = (o - xyz).length
|
||||
return Ray(o, d, min_dist=min_dist, max_dist=dist+max_dist_offset)
|
||||
|
||||
def size2D_to_size(self, size2D:float, depth:float):
|
||||
# computes size of 3D object at distance (depth) as it projects to 2D size
|
||||
# TODO: there are more efficient methods of computing this!
|
||||
|
||||
# find center of screen
|
||||
xy = Vec2D((self.actions.region.width, self.actions.region.height)) * 0.5
|
||||
# note: scaling then unscaling helps with numerical instability when clip_start is small
|
||||
scale = 1000.0
|
||||
p3d0 = self.Point2D_to_Point(xy, depth)
|
||||
p3d1 = self.Point2D_to_Point(xy + Vec2D((0, scale * size2D)), depth)
|
||||
if not p3d0 or not p3d1: return None
|
||||
return (p3d0 - p3d1).length / scale
|
||||
|
||||
def size_to_size2D(self, size:float, xyz:Point):
|
||||
if not xyz: return None
|
||||
xy = self.Point_to_Point2D(xyz)
|
||||
if not xy: return None
|
||||
pt2D = self.Point_to_Point2D(xyz - self.Vec_up() * size)
|
||||
if not pt2D: return None
|
||||
return abs(xy.y - pt2D.y)
|
||||
|
||||
def Point2D_in_area(self, p2D):
|
||||
return p2D and (0 <= p2D.x <= self.actions.size.x) and (0 <= p2D.y <= self.actions.size.y)
|
||||
|
||||
|
||||
#############################################
|
||||
# return camera up and right vectors
|
||||
|
||||
def Vec_up(self):
|
||||
# TODO: remove invert!
|
||||
return self.actions.r3d.view_matrix.to_3x3().inverted_safe() @ Vector((0,1,0))
|
||||
|
||||
def Vec_right(self):
|
||||
# TODO: remove invert!
|
||||
return self.actions.r3d.view_matrix.to_3x3().inverted_safe() @ Vector((1,0,0))
|
||||
|
||||
def Vec_forward(self):
|
||||
# TODO: remove invert!
|
||||
return self.actions.r3d.view_matrix.to_3x3().inverted_safe() @ Vector((0,0,-1))
|
||||
Reference in New Issue
Block a user