Update for 2.8
[blender-addons.git] / uv_magic_uv / op / texture_projection.py
blob77a81aa0a0162b1672d337f143edaf73cdc3b531
1 # <pep8-80 compliant>
3 # ##### BEGIN GPL LICENSE BLOCK #####
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 # ##### END GPL LICENSE BLOCK #####
21 __author__ = "Nutti <nutti.metro@gmail.com>"
22 __status__ = "production"
23 __version__ = "5.1"
24 __date__ = "24 Feb 2018"
26 from collections import namedtuple
28 import bpy
29 import bgl
30 import bmesh
31 import mathutils
32 from bpy_extras import view3d_utils
34 from .. import common
37 Rect = namedtuple('Rect', 'x0 y0 x1 y1')
38 Rect2 = namedtuple('Rect2', 'x y width height')
41 def get_canvas(context, magnitude):
42 """
43 Get canvas to be renderred texture
44 """
45 sc = context.scene
46 prefs = context.user_preferences.addons["uv_magic_uv"].preferences
48 region_w = context.region.width
49 region_h = context.region.height
50 canvas_w = region_w - prefs.texproj_canvas_padding[0] * 2.0
51 canvas_h = region_h - prefs.texproj_canvas_padding[1] * 2.0
53 img = bpy.data.images[sc.muv_texproj_tex_image]
54 tex_w = img.size[0]
55 tex_h = img.size[1]
57 center_x = region_w * 0.5
58 center_y = region_h * 0.5
60 if sc.muv_texproj_adjust_window:
61 ratio_x = canvas_w / tex_w
62 ratio_y = canvas_h / tex_h
63 if sc.muv_texproj_apply_tex_aspect:
64 ratio = ratio_y if ratio_x > ratio_y else ratio_x
65 len_x = ratio * tex_w
66 len_y = ratio * tex_h
67 else:
68 len_x = canvas_w
69 len_y = canvas_h
70 else:
71 if sc.muv_texproj_apply_tex_aspect:
72 len_x = tex_w * magnitude
73 len_y = tex_h * magnitude
74 else:
75 len_x = region_w * magnitude
76 len_y = region_h * magnitude
78 x0 = int(center_x - len_x * 0.5)
79 y0 = int(center_y - len_y * 0.5)
80 x1 = int(center_x + len_x * 0.5)
81 y1 = int(center_y + len_y * 0.5)
83 return Rect(x0, y0, x1, y1)
86 def rect_to_rect2(rect):
87 """
88 Convert Rect1 to Rect2
89 """
91 return Rect2(rect.x0, rect.y0, rect.x1 - rect.x0, rect.y1 - rect.y0)
94 def region_to_canvas(rg_vec, canvas):
95 """
96 Convert screen region to canvas
97 """
99 cv_rect = rect_to_rect2(canvas)
100 cv_vec = mathutils.Vector()
101 cv_vec.x = (rg_vec.x - cv_rect.x) / cv_rect.width
102 cv_vec.y = (rg_vec.y - cv_rect.y) / cv_rect.height
104 return cv_vec
107 class MUV_TexProjRenderer(bpy.types.Operator):
109 Operation class: Render selected texture
110 No operation (only rendering texture)
113 bl_idname = "uv.muv_texproj_renderer"
114 bl_description = "Render selected texture"
115 bl_label = "Texture renderer"
117 __handle = None
119 @staticmethod
120 def handle_add(obj, context):
121 MUV_TexProjRenderer.__handle = bpy.types.SpaceView3D.draw_handler_add(
122 MUV_TexProjRenderer.draw_texture,
123 (obj, context), 'WINDOW', 'POST_PIXEL')
125 @staticmethod
126 def handle_remove():
127 if MUV_TexProjRenderer.__handle is not None:
128 bpy.types.SpaceView3D.draw_handler_remove(
129 MUV_TexProjRenderer.__handle, 'WINDOW')
130 MUV_TexProjRenderer.__handle = None
132 @staticmethod
133 def draw_texture(_, context):
134 sc = context.scene
136 # no textures are selected
137 if sc.muv_texproj_tex_image == "None":
138 return
140 # get texture to be renderred
141 img = bpy.data.images[sc.muv_texproj_tex_image]
143 # setup rendering region
144 rect = get_canvas(context, sc.muv_texproj_tex_magnitude)
145 positions = [
146 [rect.x0, rect.y0],
147 [rect.x0, rect.y1],
148 [rect.x1, rect.y1],
149 [rect.x1, rect.y0]
151 tex_coords = [
152 [0.0, 0.0],
153 [0.0, 1.0],
154 [1.0, 1.0],
155 [1.0, 0.0]
158 # OpenGL configuration
159 bgl.glEnable(bgl.GL_BLEND)
160 bgl.glEnable(bgl.GL_TEXTURE_2D)
161 if img.bindcode:
162 bind = img.bindcode[0]
163 bgl.glBindTexture(bgl.GL_TEXTURE_2D, bind)
164 bgl.glTexParameteri(
165 bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_LINEAR)
166 bgl.glTexParameteri(
167 bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_LINEAR)
168 bgl.glTexEnvi(
169 bgl.GL_TEXTURE_ENV, bgl.GL_TEXTURE_ENV_MODE, bgl.GL_MODULATE)
171 # render texture
172 bgl.glBegin(bgl.GL_QUADS)
173 bgl.glColor4f(1.0, 1.0, 1.0, sc.muv_texproj_tex_transparency)
174 for (v1, v2), (u, v) in zip(positions, tex_coords):
175 bgl.glTexCoord2f(u, v)
176 bgl.glVertex2f(v1, v2)
177 bgl.glEnd()
180 class MUV_TexProjStart(bpy.types.Operator):
182 Operation class: Start Texture Projection
185 bl_idname = "uv.muv_texproj_start"
186 bl_label = "Start Texture Projection"
187 bl_description = "Start Texture Projection"
188 bl_options = {'REGISTER', 'UNDO'}
190 def execute(self, context):
191 props = context.scene.muv_props.texproj
192 if props.running is False:
193 MUV_TexProjRenderer.handle_add(self, context)
194 props.running = True
195 if context.area:
196 context.area.tag_redraw()
198 return {'FINISHED'}
201 class MUV_TexProjStop(bpy.types.Operator):
203 Operation class: Stop Texture Projection
206 bl_idname = "uv.muv_texproj_stop"
207 bl_label = "Stop Texture Projection"
208 bl_description = "Stop Texture Projection"
209 bl_options = {'REGISTER', 'UNDO'}
211 def execute(self, context):
212 props = context.scene.muv_props.texproj
213 if props.running is True:
214 MUV_TexProjRenderer.handle_remove()
215 props.running = False
216 if context.area:
217 context.area.tag_redraw()
219 return {'FINISHED'}
222 class MUV_TexProjProject(bpy.types.Operator):
224 Operation class: Project texture
227 bl_idname = "uv.muv_texproj_project"
228 bl_label = "Project Texture"
229 bl_description = "Project Texture"
230 bl_options = {'REGISTER', 'UNDO'}
232 @classmethod
233 def poll(cls, context):
234 obj = context.active_object
235 return obj is not None and obj.type == "MESH"
237 def execute(self, context):
238 sc = context.scene
240 if sc.muv_texproj_tex_image == "None":
241 self.report({'WARNING'}, "No textures are selected")
242 return {'CANCELLED'}
244 _, region, space = common.get_space(
245 'VIEW_3D', 'WINDOW', 'VIEW_3D')
247 # get faces to be texture projected
248 obj = context.active_object
249 world_mat = obj.matrix_world
250 bm = bmesh.from_edit_mesh(obj.data)
251 if common.check_version(2, 73, 0) >= 0:
252 bm.faces.ensure_lookup_table()
254 # get UV and texture layer
255 if not bm.loops.layers.uv:
256 if sc.muv_texproj_assign_uvmap:
257 bm.loops.layers.uv.new()
258 else:
259 self.report({'WARNING'},
260 "Object must have more than one UV map")
261 return {'CANCELLED'}
263 uv_layer = bm.loops.layers.uv.verify()
264 tex_layer = bm.faces.layers.tex.verify()
266 sel_faces = [f for f in bm.faces if f.select]
268 # transform 3d space to screen region
269 v_screen = [
270 view3d_utils.location_3d_to_region_2d(
271 region,
272 space.region_3d,
273 world_mat * l.vert.co)
274 for f in sel_faces for l in f.loops
277 # transform screen region to canvas
278 v_canvas = [
279 region_to_canvas(
281 get_canvas(bpy.context, sc.muv_texproj_tex_magnitude))
282 for v in v_screen
285 # project texture to object
286 i = 0
287 for f in sel_faces:
288 f[tex_layer].image = bpy.data.images[sc.muv_texproj_tex_image]
289 for l in f.loops:
290 l[uv_layer].uv = v_canvas[i].to_2d()
291 i = i + 1
293 common.redraw_all_areas()
294 bmesh.update_edit_mesh(obj.data)
296 return {'FINISHED'}