Files
blender-portable-repo/scripts/addons/RetopoFlow/retopoflow/rf/rf_drawing.py
T
2026-03-17 14:30:01 -06:00

306 lines
13 KiB
Python

'''
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 os
import bpy
import math
import time
import urllib
import gpu
from mathutils import Vector, Matrix
from gpu_extras.presets import draw_texture_2d
from ...addon_common.cookiecutter.cookiecutter import CookieCutter
from ...addon_common.common import gpustate
from ...addon_common.common.drawing import DrawCallbacks
from ...addon_common.common.globals import Globals
from ...addon_common.common.profiler import profiler
from ...addon_common.common.debug import tprint
from ...addon_common.common.fsm import FSM
from ...addon_common.common.hasher import Hasher
from ...addon_common.common.maths import Point, Point2D, Vec2D, XForm, clamp
from ...addon_common.common.maths import matrix_normal, Direction
from ...addon_common.terminal.term_printer import sprint
from ...config.options import options, visualization
class RetopoFlow_Drawing:
def get_view_version(self):
return Hasher(self.actions.r3d.view_matrix, self.actions.space.lens, self.actions.r3d.view_distance, self.actions.area.width, self.actions.area.height)
def setup_drawing(self):
def callback():
Globals.drawing.update_dpi()
source_opts = visualization.get_source_settings()
target_opts = visualization.get_target_settings()
self.rftarget_draw.replace_opts(target_opts)
# self.document.body.dirty(cause='--> options changed', children=True)
for d in self.rfsources_draw: d.replace_opts(source_opts)
options.add_callback(callback)
self._draw_count = 0
@DrawCallbacks.on_predraw()
def predraw(self):
if not self.loading_done: return
self.update(timer=False)
self._draw_count += 1
def get_view_matrix(self):
return self.actions.r3d.view_matrix
def get_projection_matrix(self):
return None
# r3d = self.actions.r3d
# if r3d.view_perspective == 'ORTHO':
# xdelta = right - left
# ydelta = top - bottom
# zdelta = farclip - nearclip
# return Matrix([
# [2 / xdelta, 0, 0, -(right + left) / xdelta],
# [0, 2 / ydelta, 0, -(top + bottom) / ydelta],
# [0, 0, -2 / zdelta, -(farclip + nearclip) / zdelta],
# [0, 0, 0, 1],
# ])
# else:
# return
# @DrawCallbacks.on_draw('post2d')
def draw_selection_buffer(self):
return
if not self.scene.camera: return
sprint(hasattr(self, '_gpuoffscreen'))
if not hasattr(self, '_gpuoffscreen'):
self._gpuoffscreen = gpu.types.GPUOffScreen(1, 1) #, format='RGBA8')
self._gpuoffscreen_draw_count = -1
if not hasattr(self, '_draw_count'):
# setup not fully complete yet!
return
if self._gpuoffscreen_draw_count == self._draw_count: return
w, h = int(self.actions.size.x), int(self.actions.size.y)
if self._gpuoffscreen.width != w or self._gpuoffscreen.height != h:
sprint(f'RESIZING')
self._gpuoffscreen.free()
self._gpuoffscreen = gpu.types.GPUOffScreen(w, h) #, format='RGBA8')
sprint(f'DRAWING GPUOFFSCREEN')
self._gpuoffscreen_draw_count = self._draw_count
print(self.scene.camera)
print(self.scene.camera.matrix_world)
view_mat = self.scene.camera.matrix_world.inverted_safe()
depsgraph = self.context.view_layer.depsgraph # self.context.evaluated_depsgraph_get()
proj_mat = self.scene.camera.calc_matrix_camera(depsgraph, x=w, y=h)
print(view_mat)
print(proj_mat)
with self._gpuoffscreen.bind():
self._gpuoffscreen.draw_view3d(
self.scene, # scene to draw
self.view_layer, # view layer to draw
self.actions.space, # 3D view to get the drawing settings from
self.actions.region, # Region of the 3D view
view_mat, # view matrix
proj_mat, # projection matrix
do_color_management=False,
draw_background=False,
)
gpustate.depth_mask(False)
gpustate.blend('ALPHA')
draw_texture_2d(
self._gpuoffscreen.texture_color,
(0, 0),
w, h,
)
@DrawCallbacks.on_draw('post3d')
def draw_target_and_sources(self):
if not self.actions.r3d: return
if not self.loading_done: return
# if self.fps_low_warning: return # skip drawing if low FPS warning is showing
buf_matrix_target = self.rftarget_draw.rfmesh.xform.mx_p
buf_matrix_target_inv = self.rftarget_draw.rfmesh.xform.imx_p
buf_matrix_view = self.actions.r3d.view_matrix
buf_matrix_view_invtrans = matrix_normal(self.actions.r3d.view_matrix)
buf_matrix_proj = self.actions.r3d.window_matrix
view_forward = self.Vec_forward()
gpustate.blend('ALPHA')
if options['symmetry view'] != 'None' and self.rftarget.mirror_mod.xyz:
if options['symmetry view'] in {'Edge', 'Face'}:
# get frame of target, used for symmetry decorations on sources
ft = self.rftarget.get_frame()
# render sources
for rs,rfs in zip(self.rfsources, self.rfsources_draw):
rfs.draw(
view_forward, self.unit_scaling_factor,
buf_matrix_target, buf_matrix_target_inv,
buf_matrix_view, buf_matrix_view_invtrans, buf_matrix_proj,
1.00, 0.05, # alpha above, alpha below
False, 0.5, # cull backfaces, alpha_backfaces
False, # draw mirrored
symmetry=self.rftarget.mirror_mod.xyz,
symmetry_view=options['symmetry view'],
symmetry_effect=options['symmetry effect'],
symmetry_frame=ft,
)
elif options['symmetry view'] == 'Plane':
# draw symmetry planes
gpustate.depth_test('LESS_EQUAL')
gpustate.culling('NONE')
drawing = Globals.drawing
a = pow(options['symmetry effect'], 2.0) # fudge this value, because effect is different with plane than edge/face
r = (1.0, 0.2, 0.2, a)
g = (0.2, 1.0, 0.2, a)
b = (0.2, 0.2, 1.0, a)
w2l = self.rftarget_draw.rfmesh.xform.w2l_point
l2w = self.rftarget_draw.rfmesh.xform.l2w_point
# for rfs in self.rfsources:
# corners = [self.Point_to_Point2D(l2w(p)) for p in rfs.get_local_bbox(w2l).corners]
# drawing.draw2D_lines(corners, (1,1,1,1))
corners = [ c for s in self.rfsources for c in s.get_local_bbox(w2l).corners ]
mx, Mx = min(c.x for c in corners), max(c.x for c in corners)
my, My = min(c.y for c in corners), max(c.y for c in corners)
mz, Mz = min(c.z for c in corners), max(c.z for c in corners)
cx, cy, cz = mx + (Mx - mx) / 2, my + (My - my) / 2, mz + (Mz - mz) / 2
mx, Mx = cx + (mx - cx) * 1.2, cx + (Mx - cx) * 1.2
my, My = cy + (my - cy) * 1.2, cy + (My - cy) * 1.2
mz, Mz = cz + (mz - cz) * 1.2, cz + (Mz - cz) * 1.2
if self.rftarget.mirror_mod.x:
quad = [ l2w(Point((0, my, mz))), l2w(Point((0, my, Mz))), l2w(Point((0, My, Mz))), l2w(Point((0, My, mz))) ]
drawing.draw3D_triangles([quad[0], quad[1], quad[2], quad[0], quad[2], quad[3]], [r, r, r, r, r, r])
if self.rftarget.mirror_mod.y:
quad = [ l2w(Point((mx, 0, mz))), l2w(Point((mx, 0, Mz))), l2w(Point((Mx, 0, Mz))), l2w(Point((Mx, 0, mz))) ]
drawing.draw3D_triangles([quad[0], quad[1], quad[2], quad[0], quad[2], quad[3]], [g, g, g, g, g, g])
if self.rftarget.mirror_mod.z:
quad = [ l2w(Point((mx, my, 0))), l2w(Point((mx, My, 0))), l2w(Point((Mx, My, 0))), l2w(Point((Mx, my, 0))) ]
drawing.draw3D_triangles([quad[0], quad[1], quad[2], quad[0], quad[2], quad[3]], [b, b, b, b, b, b])
# render target
gpustate.blend('ALPHA')
if True:
alpha_above,alpha_below = options['target alpha'],options['target hidden alpha']
cull_backfaces = options['target cull backfaces']
alpha_backface = options['target alpha backface']
self.rftarget_draw.draw(
view_forward, self.unit_scaling_factor,
buf_matrix_target, buf_matrix_target_inv,
buf_matrix_view, buf_matrix_view_invtrans, buf_matrix_proj,
alpha_above, alpha_below,
cull_backfaces, alpha_backface,
True, # draw_mirrored
)
@DrawCallbacks.on_draw('post3d')
def draw_greasemarks(self):
return
# if not self.actions.r3d: return
# # THE FOLLOWING CODE NEEDS UPDATED TO NOT USE GLBEGIN!
# # grease marks
# b_g_l.glBegin(b_g_l.GL_QUADS)
# for stroke_data in self.grease_marks:
# b_g_l.glColor4f(*stroke_data['color'])
# t = stroke_data['thickness']
# s0,p0,n0,d0,d1 = None,None,None,None,None
# for s1 in stroke_data['marks']:
# p1,n1 = s1
# if p0 and p1:
# v01 = p1 - p0
# if d0 is None: d0 = Direction(v01.cross(n0))
# d1 = Direction(v01.cross(n1))
# b_g_l.glVertex3f(*(p0-d0*t+n0*0.001))
# b_g_l.glVertex3f(*(p0+d0*t+n0*0.001))
# b_g_l.glVertex3f(*(p1+d1*t+n1*0.001))
# b_g_l.glVertex3f(*(p1-d1*t+n1*0.001))
# s0,p0,n0,d0 = s1,p1,n1,d1
# b_g_l.glEnd()
##################################
# RFTool Drawing
@DrawCallbacks.on_draw('predraw')
@FSM.onlyinstate({'main', 'quick switch'})
def tool_new_frame(self):
if not self.loading_done: return
# if self.fsm.state == 'pie menu': return
self.rftool._new_frame()
@DrawCallbacks.on_draw('pre3d')
@FSM.onlyinstate({'main', 'quick switch'})
def draw_tool_pre3d(self):
if not self.loading_done: return
# if self.fsm.state == 'pie menu': return
self.rftool._draw_pre3d()
@DrawCallbacks.on_draw('post3d')
@FSM.onlyinstate({'main', 'quick switch'})
def draw_tool_post3d(self):
if not self.loading_done: return
# if self.fsm.state == 'pie menu': return
self.rftool._draw_post3d()
@DrawCallbacks.on_draw('post2d')
@FSM.onlyinstate({'main', 'quick switch'})
def draw_tool_post2d(self):
if not self.loading_done: return
# if self.fsm.state == 'pie menu': return
self.rftool._draw_post2d()
#############################
# RFWidget Drawing
@DrawCallbacks.on_draw('pre3d')
@FSM.onlyinstate({'main', 'quick switch'})
def draw_widget_pre3d(self):
if not self.loading_done: return
if not self.rftool.rfwidget: return
if self._nav: return
if self._hover_ui: return
# if self.fsm.state == 'pie menu': return
self.rftool.rfwidget._draw_pre3d()
@DrawCallbacks.on_draw('post3d')
@FSM.onlyinstate({'main', 'quick switch'})
def draw_widget_post3d(self):
if not self.loading_done: return
if not self.rftool.rfwidget: return
if self._nav: return
if self._hover_ui: return
# if self.fsm.state == 'pie menu': return
self.rftool.rfwidget._draw_post3d()
@DrawCallbacks.on_draw('post2d')
@FSM.onlyinstate({'main', 'quick switch'})
def draw_widget_post2d(self):
if not self.loading_done: return
if not self.rftool.rfwidget: return
if self._nav: return
if self._hover_ui: return
# if self.fsm.state == 'pie menu': return
self.rftool.rfwidget._draw_post2d()