Skip to content

Commit

Permalink
Merge pull request #25 from mpcarr/master
Browse files Browse the repository at this point in the history
Fixes issue with aoi, unwrapped bake objects and adds fit pf camera support back in.
  • Loading branch information
vbousquet authored Mar 11, 2024
2 parents f9e9a82 + c7e3c59 commit 840b108
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 0 deletions.
31 changes: 31 additions & 0 deletions addons/vpx_lightmapper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down
118 changes: 118 additions & 0 deletions addons/vpx_lightmapper/vlm_camera.py
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>

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)

6 changes: 6 additions & 0 deletions addons/vpx_lightmapper/vlm_render_baker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])


Expand Down Expand Up @@ -753,6 +755,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)
Expand Down

0 comments on commit 840b108

Please sign in to comment.