From 0ab1d9a78d458c6762e35d109a9741c5769db2ff Mon Sep 17 00:00:00 2001 From: Matt Carr Date: Thu, 22 Feb 2024 13:35:56 +0000 Subject: [PATCH 1/3] fix: issue with unwrapped camera bake objects not casting shadows --- addons/vpx_lightmapper/vlm_render_baker.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/vpx_lightmapper/vlm_render_baker.py b/addons/vpx_lightmapper/vlm_render_baker.py index 1270560..0298f16 100644 --- a/addons/vpx_lightmapper/vlm_render_baker.py +++ b/addons/vpx_lightmapper/vlm_render_baker.py @@ -753,6 +753,10 @@ def sortkey(x): for node in ti: mat.node_tree.nodes.remove(node) bpy.data.images.remove(bake_img) + + if not dup.vlmSettings.hide_from_others: + indirect_col.objects.link(dup) + if dup.vlmSettings.bake_mask: render_col.objects.unlink(dup.vlmSettings.bake_mask) render_col.objects.unlink(dup) From a0b5a206d29cf7aa9abfb70ed71c04aea0f3961b Mon Sep 17 00:00:00 2001 From: Matt Carr Date: Thu, 22 Feb 2024 13:36:44 +0000 Subject: [PATCH 2/3] feat: add back in fit to pf option for convenient camera placement --- addons/vpx_lightmapper/__init__.py | 31 +++++++ addons/vpx_lightmapper/vlm_camera.py | 118 +++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 addons/vpx_lightmapper/vlm_camera.py diff --git a/addons/vpx_lightmapper/__init__.py b/addons/vpx_lightmapper/__init__.py index ab1a208..45a1660 100644 --- a/addons/vpx_lightmapper/__init__.py +++ b/addons/vpx_lightmapper/__init__.py @@ -56,6 +56,10 @@ importlib.reload(vlm_occlusion) else: from . import vlm_occlusion +if "vlm_camera" in locals(): + importlib.reload(vlm_camera) +else: + from . import vlm_camera logger = vlm_utils.logger @@ -152,6 +156,18 @@ class VLM_Scene_props(PropertyGroup): use_pf_translucency_map: BoolProperty(name="Translucency Map", description="Generate a translucency map for inserts", default = True) process_plastics: BoolProperty(name="Convert plastics", description="Detect plastics and converts them", default = True) bevel_plastics: FloatProperty(name="Bevel plastics", description="Bevel converted plastics", default = 1.0) + # Camera options + camera_inclination: FloatProperty(name="Inclination", description="Camera inclination", default = 15.0, update=vlm_camera.camera_inclination_update) + camera_layback: FloatProperty(name="Layback", description="Camera layback", default = 35.0, update=vlm_camera.camera_inclination_update) + layback_mode: EnumProperty( + items=[ + ('disable', 'Disable', 'Disable layback', '', 0), + ('fit_pf', 'Fit PF', 'Fit camera to playfield.', '', 3) + ], + name='Layback mode', + default='fit_pf', + update=vlm_camera.camera_inclination_update + ) # Baker options batch_inc_group: BoolProperty(name="Perform Group", description="Perform Group step when batching", default = True) batch_shutdown: BoolProperty(name="Shutdown", description="Shutdown computer after batch", default = False) @@ -748,6 +764,20 @@ def draw(self, context): layout.prop(vlmProps, "use_pf_translucency_map") +class VLM_PT_Camera(bpy.types.Panel): + bl_label = "VPX Camera" + bl_category = "VLM" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "scene" + def draw(self, context): + layout = self.layout + layout.use_property_split = True + vlmProps = context.scene.vlmSettings + layout.prop(vlmProps, "layback_mode", expand=True) + layout.prop(vlmProps, "camera_layback") + layout.prop(vlmProps, "camera_inclination") + class VLM_PT_Lightmapper(bpy.types.Panel): bl_label = "VPX Light Mapper" bl_category = "VLM" @@ -1206,6 +1236,7 @@ def draw(self, context): VLM_Collection_props, VLM_Object_props, VLM_PT_Importer, + VLM_PT_Camera, VLM_PT_Lightmapper, VLM_PT_Col_Props, VLM_PT_3D_VPX_Import, diff --git a/addons/vpx_lightmapper/vlm_camera.py b/addons/vpx_lightmapper/vlm_camera.py new file mode 100644 index 0000000..287b13f --- /dev/null +++ b/addons/vpx_lightmapper/vlm_camera.py @@ -0,0 +1,118 @@ +# Copyright (C) 2022 Vincent Bousquet +# +# 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 + +import bpy +import array +import os +import pathlib +import gpu +import math +import mathutils +import functools +from gpu_extras.presets import draw_texture_2d +from gpu_extras.batch import batch_for_shader + +from . import vlm_utils +from . import vlm_collections + + +def camera_inclination_update(op, context): + """Update bake camera position based on its inclination, in order to fit the following constraints: + - look at the center of the playfield + - view all baked objects + - satisfy the target texture size on the vertical axis (height of the render) + + There are 3 way to take layback in account: + - disable: no layback, just normal fitting. + - deform: deform geometry (like in VPX) and perform normal fitting on the deformed geometry. + - camera: take layback in account in camera orientation (for correct shading and face visibility), + and overscale image on the y axis (for bake texture density) corresponding to what it would have been + if we have deformed the geometry (like in VPX) + """ + camera_object = context.scene.camera + bake_col = vlm_collections.get_collection(context.scene.collection, 'VLM.Bake', create=False) + if not camera_object or not bake_col: + print("Warning: Missing needed elements to fit the camera (camera or bake collection)") + return + + print(f'Fitting camera to bake objects') + + # Adjust the camera + context.scene.render.pixel_aspect_x = 1 + layback_mode = context.scene.vlmSettings.layback_mode + if layback_mode == 'disable': + camera_inclination = context.scene.vlmSettings.camera_inclination + camera_layback = 0 + fit_camera(context, camera_object, camera_inclination, camera_layback, bake_col) + elif layback_mode == 'fit_pf': + # Compute deform camera parameter (aspect ratio and resulting render width) using an adjusted camera inclination instead + camera_inclination = context.scene.vlmSettings.camera_inclination + context.scene.vlmSettings.camera_layback / 2 + camera_layback = 0 + fit_camera(context, camera_object, camera_inclination, camera_layback, bake_col) + + +def fit_camera(context, camera_object, camera_inclination, camera_layback, bake_col): + camera_fov = camera_object.data.angle + playfield_left = 0.0 + playfield_top = 0.0 + global_scale = vlm_utils.get_global_scale(context) + playfield_height = (context.scene.vlmSettings.playfield_height * global_scale) / (1.0625 / 50.0) + playfield_width = (context.scene.vlmSettings.playfield_width * global_scale) / (1.0625 / 50.0) + layback = mathutils.Matrix.Shear('XY', 4, (0, math.tan(math.radians(camera_layback) / 2))) + camera_angle = math.radians(camera_inclination) + camera_object.rotation_euler = mathutils.Euler((camera_angle, 0.0, 0.0), 'XYZ') + camera_object.data.shift_x = 0 + camera_object.data.shift_y = 0 + view_vector = mathutils.Vector((0, math.sin(camera_angle), -math.cos(camera_angle))) + aspect_ratio = 1.0 + for i in range(3): # iterations since it depends on the aspect ratio fitting which change after each computation + # Compute the camera distance with the current aspect ratio + camera_object.location = (playfield_left + 0.5 * playfield_width, -playfield_top -0.5 * playfield_height, 0) + modelview_matrix = camera_object.matrix_basis.inverted() + s = 1.0 / math.tan(camera_fov/2.0) + sx = s if aspect_ratio > 1.0 else s/aspect_ratio + sy = s if aspect_ratio < 1.0 else s*aspect_ratio + min_dist = 0 + for obj in bake_col.all_objects: + if obj.type == 'MESH': # and not obj.hide_get() and not obj.hide_render: + bbox_corners = [modelview_matrix @ obj.matrix_world @ layback @ mathutils.Vector(corner) for corner in obj.bound_box] + proj_x = map(lambda a: abs(sx * a.x + a.z), bbox_corners) + proj_y = map(lambda a: abs(sy * a.y + a.z), bbox_corners) + min_dist = max(min_dist, max(proj_x), max(proj_y)) + camera_object.location.y -= min_dist * view_vector.y + camera_object.location.z -= min_dist * view_vector.z + # adjust aspect ratio and compute camera shift to fill the render output + modelview_matrix = camera_object.matrix_basis.inverted() + projection_matrix = camera_object.calc_matrix_camera(context.evaluated_depsgraph_get()) + max_x = max_y = -10000000 + min_x = min_y = 10000000 + for obj in bake_col.all_objects: + if obj.type == 'MESH': # and not obj.hide_get() and not obj.hide_render: + bbox_corners = [projection_matrix @ modelview_matrix @ obj.matrix_world @ layback @ mathutils.Vector((corner[0], corner[1], corner[2], 1)) for corner in obj.bound_box] + proj_x = [o for o in map(lambda a: a.x / a.w, bbox_corners)] + proj_y = [o for o in map(lambda a: a.y / a.w, bbox_corners)] + min_x = min(min_x, min(proj_x)) + min_y = min(min_y, min(proj_y)) + max_x = max(max_x, max(proj_x)) + max_y = max(max_y, max(proj_y)) + aspect_ratio = (max_x - min_x) / (max_y - min_y) + render_size = vlm_utils.get_render_size(context) + context.scene.render.resolution_x = int(render_size[1] * aspect_ratio) + context.scene.render.resolution_y = render_size[1] + + # Center on render output + camera_object.data.shift_x = 0.25 * (max_x + min_x) + camera_object.data.shift_y = 0.25 * (max_y + min_y) + From c7e3c5946bf5b77c4a6c2d0d6b308746fab73bc1 Mon Sep 17 00:00:00 2001 From: Matt Carr Date: Sun, 10 Mar 2024 19:55:53 +0000 Subject: [PATCH 3/3] fix: aoi when homogeneous w is behind the camera --- addons/vpx_lightmapper/vlm_render_baker.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/vpx_lightmapper/vlm_render_baker.py b/addons/vpx_lightmapper/vlm_render_baker.py index 0298f16..09312f7 100644 --- a/addons/vpx_lightmapper/vlm_render_baker.py +++ b/addons/vpx_lightmapper/vlm_render_baker.py @@ -35,6 +35,8 @@ def project_point(proj, p): p1 = proj @ Vector((p.x, p.y, p.z, 1)) # projected coordinates (range [-1, 1]x[-1, 1]) + if p1.w<=0: + return Vector((1,1)) return Vector(((1 + p1.x / p1.w) / 2, (1 - p1.y / p1.w) / 2)) # pixel coordinates (range [0, 1]x[0, 1])