diff options
Diffstat (limited to 'archipack/archipack_rendering.py')
-rw-r--r-- | archipack/archipack_rendering.py | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/archipack/archipack_rendering.py b/archipack/archipack_rendering.py new file mode 100644 index 00000000..3d86d4d8 --- /dev/null +++ b/archipack/archipack_rendering.py @@ -0,0 +1,529 @@ +# -*- coding:utf-8 -*- + +# ##### 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 ##### + +# <pep8 compliant> + +# ---------------------------------------------------------- +# support routines for render measures in final image +# Author: Antonio Vazquez (antonioya) +# Archipack adaptation by : Stephen Leger (s-leger) +# +# ---------------------------------------------------------- +# noinspection PyUnresolvedReferences +import bpy +# noinspection PyUnresolvedReferences +import bgl +from os import path, remove +from sys import exc_info +# noinspection PyUnresolvedReferences +import bpy_extras.image_utils as img_utils +# noinspection PyUnresolvedReferences +from math import ceil +from bpy.types import Operator + + +# ------------------------------------------------------------- +# Defines button for render +# +# ------------------------------------------------------------- +class ARCHIPACK_OT_render(Operator): + bl_idname = "archipack.render" + bl_label = "Render" + bl_category = 'Archipack' + bl_description = "Create a render image with measures. Use UV/Image editor to view image generated" + bl_category = 'Archipack' + + # -------------------------------------------------------------------- + # Get the final render image and return as image object + # + # return None if no render available + # -------------------------------------------------------------------- + + def get_render_image(self, outpath): + saved = False + # noinspection PyBroadException + try: + # noinspection PyBroadException + try: + result = bpy.data.images['Render Result'] + if result.has_data is False: + # this save produce to fill data image + result.save_render(outpath) + saved = True + except: + print("No render image found") + return None + + # Save and reload + if saved is False: + result.save_render(outpath) + + img = img_utils.load_image(outpath) + + return img + except: + print("Unexpected render image error") + return None + + # ------------------------------------- + # Save image to file + # ------------------------------------- + + def save_image(self, filepath, myimage): + # noinspection PyBroadException + try: + + # Save old info + settings = bpy.context.scene.render.image_settings + myformat = settings.file_format + mode = settings.color_mode + depth = settings.color_depth + + # Apply new info and save + settings.file_format = 'PNG' + settings.color_mode = "RGBA" + settings.color_depth = '8' + myimage.save_render(filepath) + print("Archipack: Image " + filepath + " saved") + + # Restore old info + settings.file_format = myformat + settings.color_mode = mode + settings.color_depth = depth + except: + print("Unexpected error:" + str(exc_info())) + self.report({'ERROR'}, "Archipack: Unable to save render image") + return + + # ------------------------------------------------------------- + # Render image main entry point + # + # ------------------------------------------------------------- + + def render_main(self, context, objlist, animation=False): + # noinspection PyBroadException,PyBroadException + # Save old info + scene = context.scene + render = scene.render + settings = render.image_settings + depth = settings.color_depth + settings.color_depth = '8' + # noinspection PyBroadException + try: + + # Get visible layers + layers = [] + for x in range(0, 20): + if scene.layers[x] is True: + layers.extend([x]) + + # -------------------- + # Get resolution + # -------------------- + render_scale = render.resolution_percentage / 100 + + width = int(render.resolution_x * render_scale) + height = int(render.resolution_y * render_scale) + # --------------------------------------- + # Get output path + # --------------------------------------- + temp_path = path.realpath(bpy.app.tempdir) + if len(temp_path) > 0: + outpath = path.join(temp_path, "archipack_tmp_render.png") + else: + self.report({'ERROR'}, + "Archipack: Unable to save temporary render image. Define a valid temp path") + settings.color_depth = depth + return False + + # Get Render Image + img = self.get_render_image(outpath) + if img is None: + self.report({'ERROR'}, + "Archipack: Unable to save temporary render image. Define a valid temp path") + settings.color_depth = depth + return False + + # ----------------------------- + # Calculate rows and columns + # ----------------------------- + tile_x = 240 + tile_y = 216 + row_num = ceil(height / tile_y) + col_num = ceil(width / tile_x) + print("Archipack: Image divided in " + str(row_num) + "x" + str(col_num) + " tiles") + + # pixels out of visible area + cut4 = (col_num * tile_x * 4) - width * 4 # pixels aout of drawing area + totpixel4 = width * height * 4 # total pixels RGBA + + viewport_info = bgl.Buffer(bgl.GL_INT, 4) + bgl.glGetIntegerv(bgl.GL_VIEWPORT, viewport_info) + + # Load image on memory + img.gl_load(0, bgl.GL_NEAREST, bgl.GL_NEAREST) + + # 2.77 API change + if bpy.app.version >= (2, 77, 0): + tex = img.bindcode[0] + else: + tex = img.bindcode + + # -------------------------------------------- + # Create output image (to apply texture) + # -------------------------------------------- + if "archipack_output" in bpy.data.images: + out_img = bpy.data.images["archipack_output"] + if out_img is not None: + out_img.user_clear() + bpy.data.images.remove(out_img) + + out = bpy.data.images.new("archipack_output", width, height) + tmp_pixels = [1] * totpixel4 + + # -------------------------------- + # Loop for all tiles + # -------------------------------- + for row in range(0, row_num): + for col in range(0, col_num): + buffer = bgl.Buffer(bgl.GL_FLOAT, width * height * 4) + bgl.glDisable(bgl.GL_SCISSOR_TEST) # if remove this line, get blender screenshot not image + bgl.glViewport(0, 0, tile_x, tile_y) + + bgl.glMatrixMode(bgl.GL_PROJECTION) + bgl.glLoadIdentity() + + # defines ortographic view for single tile + x1 = tile_x * col + y1 = tile_y * row + bgl.gluOrtho2D(x1, x1 + tile_x, y1, y1 + tile_y) + + # Clear + bgl.glClearColor(0.0, 0.0, 0.0, 0.0) + bgl.glClear(bgl.GL_COLOR_BUFFER_BIT | bgl.GL_DEPTH_BUFFER_BIT) + + bgl.glEnable(bgl.GL_TEXTURE_2D) + bgl.glBindTexture(bgl.GL_TEXTURE_2D, tex) + + # defines drawing area + bgl.glBegin(bgl.GL_QUADS) + + bgl.glColor3f(1.0, 1.0, 1.0) + bgl.glTexCoord2f(0.0, 0.0) + bgl.glVertex2f(0.0, 0.0) + + bgl.glTexCoord2f(1.0, 0.0) + bgl.glVertex2f(width, 0.0) + + bgl.glTexCoord2f(1.0, 1.0) + bgl.glVertex2f(width, height) + + bgl.glTexCoord2f(0.0, 1.0) + bgl.glVertex2f(0.0, height) + + bgl.glEnd() + + # ----------------------------- + # Loop to draw all lines + # ----------------------------- + for o, d in objlist: + if o.hide is False: + # verify visible layer + for x in range(0, 20): + if o.layers[x] is True: + if x in layers: + context.scene.objects.active = o + # print("%s: %s" % (o.name, d.manip_stack)) + manipulators = d.manip_stack + if manipulators is not None: + for m in manipulators: + if m is not None: + m.draw_callback(m, context, render=True) + break + + # ----------------------------- + # Loop to draw all debug + # ----------------------------- + """ + if scene.archipack_debug is True: + selobj = bpy.context.selected_objects + for myobj in selobj: + if scene.archipack_debug_vertices is True: + draw_vertices(context, myobj, None, None) + if scene.archipack_debug_faces is True or scene.archipack_debug_normals is True: + draw_faces(context, myobj, None, None) + """ + """ + if scene.archipack_rf is True: + bgl.glColor3f(1.0, 1.0, 1.0) + rfcolor = scene.archipack_rf_color + rfborder = scene.archipack_rf_border + rfline = scene.archipack_rf_line + + bgl.glLineWidth(rfline) + bgl.glColor4f(rfcolor[0], rfcolor[1], rfcolor[2], rfcolor[3]) + + x1 = rfborder + x2 = width - rfborder + y1 = int(ceil(rfborder / (width / height))) + y2 = height - y1 + draw_rectangle((x1, y1), (x2, y2)) + """ + # -------------------------------- + # copy pixels to temporary area + # -------------------------------- + bgl.glFinish() + bgl.glReadPixels(0, 0, width, height, bgl.GL_RGBA, bgl.GL_FLOAT, buffer) # read image data + for y in range(0, tile_y): + # final image pixels position + p1 = (y * width * 4) + (row * tile_y * width * 4) + (col * tile_x * 4) + p2 = p1 + (tile_x * 4) + # buffer pixels position + b1 = y * width * 4 + b2 = b1 + (tile_x * 4) + + if p1 < totpixel4: # avoid pixel row out of area + if col == col_num - 1: # avoid pixel columns out of area + p2 -= cut4 + b2 -= cut4 + + tmp_pixels[p1:p2] = buffer[b1:b2] + + # ----------------------- + # Copy temporary to final + # ----------------------- + out.pixels = tmp_pixels[:] # Assign image data + img.gl_free() # free opengl image memory + + # delete image + img.user_clear() + bpy.data.images.remove(img) + # remove temp file + remove(outpath) + # reset + bgl.glEnable(bgl.GL_SCISSOR_TEST) + # ----------------------- + # restore opengl defaults + # ----------------------- + bgl.glLineWidth(1) + bgl.glDisable(bgl.GL_BLEND) + bgl.glColor4f(0.0, 0.0, 0.0, 1.0) + # Saves image + if out is not None: + # and (scene.archipack_render is True or animation is True): + ren_path = bpy.context.scene.render.filepath + filename = "ap_frame" + if len(ren_path) > 0: + if ren_path.endswith(path.sep): + initpath = path.realpath(ren_path) + path.sep + else: + (initpath, filename) = path.split(ren_path) + + ftxt = "%04d" % scene.frame_current + outpath = path.realpath(path.join(initpath, filename + ftxt + ".png")) + + self.save_image(outpath, out) + + settings.color_depth = depth + return True + + except: + settings.color_depth = depth + print("Unexpected error:" + str(exc_info())) + self.report( + {'ERROR'}, + "Archipack: Unable to create render image. Be sure the output render path is correct" + ) + return False + + def get_objlist(self, context): + """ + Get objects with gl manipulators + """ + objlist = [] + for o in context.scene.objects: + if o.data is not None: + d = None + if 'archipack_window' in o.data: + d = o.data.archipack_window[0] + elif 'archipack_door' in o.data: + d = o.data.archipack_door[0] + elif 'archipack_wall2' in o.data: + d = o.data.archipack_wall2[0] + elif 'archipack_stair' in o.data: + d = o.data.archipack_stair[0] + elif 'archipack_fence' in o.data: + d = o.data.archipack_fence[0] + if d is not None: + objlist.append((o, d)) + return objlist + + def draw_gl(self, context): + objlist = self.get_objlist(context) + for o, d in objlist: + context.scene.objects.active = o + d.manipulable_disable(context) + d.manipulable_invoke(context) + return objlist + + def hide_gl(self, context, objlist): + for o, d in objlist: + context.scene.objects.active = o + d.manipulable_disable(context) + + # ------------------------------ + # Execute button action + # ------------------------------ + # noinspection PyMethodMayBeStatic,PyUnusedLocal + def execute(self, context): + scene = context.scene + wm = context.window_manager + msg = "New image created with measures. Open it in UV/image editor" + camera_msg = "Unable to render. No camera found" + + # ----------------------------- + # Check camera + # ----------------------------- + if scene.camera is None: + self.report({'ERROR'}, camera_msg) + return {'FINISHED'} + + objlist = self.draw_gl(context) + + # ----------------------------- + # Use current rendered image + # ----------------------------- + if wm.archipack.render_type == "1": + # noinspection PyBroadException + try: + result = bpy.data.images['Render Result'] + if result.has_data is False: + bpy.ops.render.render() + except: + bpy.ops.render.render() + + print("Archipack: Using current render image on buffer") + if self.render_main(context, objlist) is True: + self.report({'INFO'}, msg) + + # ----------------------------- + # OpenGL image + # ----------------------------- + elif wm.archipack.render_type == "2": + self.set_camera_view() + self.set_only_render(True) + + print("Archipack: Rendering opengl image") + bpy.ops.render.opengl() + if self.render_main(context, objlist) is True: + self.report({'INFO'}, msg) + + self.set_only_render(False) + + # ----------------------------- + # OpenGL Animation + # ----------------------------- + elif wm.archipack.render_type == "3": + oldframe = scene.frame_current + self.set_camera_view() + self.set_only_render(True) + flag = False + # loop frames + for frm in range(scene.frame_start, scene.frame_end + 1): + scene.frame_set(frm) + print("Archipack: Rendering opengl frame %04d" % frm) + bpy.ops.render.opengl() + flag = self.render_main(context, objlist, True) + if flag is False: + break + + self.set_only_render(False) + scene.frame_current = oldframe + if flag is True: + self.report({'INFO'}, msg) + + # ----------------------------- + # Image + # ----------------------------- + elif wm.archipack.render_type == "4": + print("Archipack: Rendering image") + bpy.ops.render.render() + if self.render_main(context, objlist) is True: + self.report({'INFO'}, msg) + + # ----------------------------- + # Animation + # ----------------------------- + elif wm.archipack.render_type == "5": + oldframe = scene.frame_current + flag = False + # loop frames + for frm in range(scene.frame_start, scene.frame_end + 1): + scene.frame_set(frm) + print("Archipack: Rendering frame %04d" % frm) + bpy.ops.render.render() + flag = self.render_main(context, objlist, True) + if flag is False: + break + + scene.frame_current = oldframe + if flag is True: + self.report({'INFO'}, msg) + + self.hide_gl(context, objlist) + + return {'FINISHED'} + + # --------------------- + # Set cameraView + # --------------------- + # noinspection PyMethodMayBeStatic + def set_camera_view(self): + for area in bpy.context.screen.areas: + if area.type == 'VIEW_3D': + area.spaces[0].region_3d.view_perspective = 'CAMERA' + + # ------------------------------------- + # Set only render status + # ------------------------------------- + # noinspection PyMethodMayBeStatic + def set_only_render(self, status): + screen = bpy.context.screen + + v3d = False + s = None + # get spaceview_3d in current screen + for a in screen.areas: + if a.type == 'VIEW_3D': + for s in a.spaces: + if s.type == 'VIEW_3D': + v3d = s + break + + if v3d is not False: + s.show_only_render = status + + +def register(): + bpy.utils.register_class(ARCHIPACK_OT_render) + + +def unregister(): + bpy.utils.unregister_class(ARCHIPACK_OT_render) |